Closed AmitKumarDas closed 2 years ago
Modify kubeconfig files using subcommands like "kubectl config set current-context my-context"
The loading order follows these rules:
1. If the --kubeconfig flag is set, then only that file is loaded. The flag may only be set once and no merging takes
place.
2. If $KUBECONFIG environment variable is set, then it is used as a list of paths (normal path delimiting rules for
your system). These paths are merged. When a value is modified, it is modified in the file that defines the stanza. When
a value is created, it is created in the first file that exists. If no files in the chain exist, then it creates the
last file in the list.
3. Otherwise, ${HOME}/.kube/config is used and no merging takes place.
// kubeconfig, client
// refer: https://github.com/banzaicloud/kurun/blob/master/kurun.go
//
func getKubeConfig() (*rest.Config, error) {
// If an env variable is specified with the config locaiton, use that
if len(os.Getenv("KUBECONFIG")) > 0 {
return clientcmd.BuildConfigFromFlags("", os.Getenv("KUBECONFIG"))
}
// If no explicit location, try the in-cluster config
if c, err := rest.InClusterConfig(); err == nil {
return c, nil
}
// If no in-cluster config, try the default location in the user's home directory
if usr, err := user.Current(); err == nil {
if c, err := clientcmd.BuildConfigFromFlags(
"", filepath.Join(usr.HomeDir, ".kube", "config")); err == nil {
return c, nil
}
}
return nil, fmt.Errorf("could not locate a kubeconfig")
}
// https://github.com/banzaicloud/bank-vaults/blob/master/operator/pkg/controller/vault/vault_controller.go
// create or update, apply
//
func createOrUpdateObjectWithClient(c client.Client, o client.Object) error {
key := client.ObjectKeyFromObject(o)
current := o.DeepCopyObject().(client.Object)
err := c.Get(context.TODO(), key, current)
if apierrors.IsNotFound(err) {
err := patch.DefaultAnnotator.SetLastAppliedAnnotation(o)
if err != nil {
log.Error(err, "failed to annotate original object", "object", o)
}
return c.Create(context.TODO(), o)
} else if err == nil {
// Handle special cases for update
switch o.(type) {
case *corev1.Service:
currentSvc := current.(*corev1.Service)
svc := o.(*corev1.Service)
// Preserve the ClusterIP when updating the service
svc.Spec.ClusterIP = currentSvc.Spec.ClusterIP
// Preserve the annotation when updating the service, ensure any updated annotation is preserved
for key, value := range currentSvc.Annotations {
if _, present := svc.Annotations[key]; !present {
svc.Annotations[key] = value
}
}
if svc.Spec.Type == corev1.ServiceTypeNodePort || svc.Spec.Type == corev1.ServiceTypeLoadBalancer {
for i := range svc.Spec.Ports {
svc.Spec.Ports[i].NodePort = currentSvc.Spec.Ports[i].NodePort
}
}
}
result, err := patch.DefaultPatchMaker.Calculate(current, o, patch.IgnoreStatusFields())
if err != nil {
log.Error(err, "failed to calculate patch to match objects, moving on to update")
// if there is an error with matching, we still want to update
resourceVersion := current.(metav1.ObjectMetaAccessor).GetObjectMeta().GetResourceVersion()
o.(metav1.ObjectMetaAccessor).GetObjectMeta().SetResourceVersion(resourceVersion)
return c.Update(context.TODO(), o)
}
if !result.IsEmpty() {
log.V(1).Info(fmt.Sprintf("Resource update for object %s:%s", o.GetObjectKind(), o.(metav1.ObjectMetaAccessor).GetObjectMeta().GetName()),
"patch", string(result.Patch),
// "original", string(result.Original),
// "modified", string(result.Modified),
// "current", string(result.Current),
)
err := patch.DefaultAnnotator.SetLastAppliedAnnotation(o)
if err != nil {
log.Error(err, "failed to annotate modified object", "object", o)
}
resourceVersion := current.(metav1.ObjectMetaAccessor).GetObjectMeta().GetResourceVersion()
o.(metav1.ObjectMetaAccessor).GetObjectMeta().SetResourceVersion(resourceVersion)
return c.Update(context.TODO(), o)
}
log.V(1).Info(fmt.Sprintf("Skipping update for object %s:%s", o.GetObjectKind(), o.(metav1.ObjectMetaAccessor).GetObjectMeta().GetName()))
}
return err
}
// https://github.com/kubernetes-sigs/controller-runtime/blob/master/pkg/client/example_test.go
//
// This example shows how to use the client with typed and unstructured objects to patch objects.
// raw patch, two way merge, strategic merge patch
//
func ExampleClient_patch() {
patch := []byte(`{"metadata":{"annotations":{"version": "v2"}}}`)
_ = c.Patch(context.Background(), &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Namespace: "namespace",
Name: "name",
},
}, client.RawPatch(types.StrategicMergePatchType, patch))
}
// This example shows how to use the client with typed and unstructured objects to patch objects' status.
func ExampleClient_patchStatus() {
u := &unstructured.Unstructured{}
u.Object = map[string]interface{}{
"metadata": map[string]interface{}{
"name": "foo",
"namespace": "namespace",
},
}
u.SetGroupVersionKind(schema.GroupVersionKind{
Group: "batch",
Version: "v1beta1",
Kind: "CronJob",
})
patch := []byte(fmt.Sprintf(`{"status":{"lastScheduleTime":"%s"}}`, time.Now().Format(time.RFC3339)))
_ = c.Status().Patch(context.Background(), u, client.RawPatch(types.MergePatchType, patch))
}
// https://github.com/kubernetes/apimachinery/blob/v0.22.1/pkg/apis/meta/v1/types.go
//
// PatchOptions may be provided when patching an API object.
// PatchOptions is meant to be a superset of UpdateOptions.
type PatchOptions struct {
TypeMeta `json:",inline"`
// When present, indicates that modifications should not be
// persisted. An invalid or unrecognized dryRun directive will
// result in an error response and no further processing of the
// request. Valid values are:
// - All: all dry run stages will be processed
// +optional
DryRun []string `json:"dryRun,omitempty" protobuf:"bytes,1,rep,name=dryRun"`
// Force is going to "force" Apply requests. It means user will
// re-acquire conflicting fields owned by other people. Force
// flag must be unset for non-apply patch requests.
// +optional
Force *bool `json:"force,omitempty" protobuf:"varint,2,opt,name=force"`
// fieldManager is a name associated with the actor or entity
// that is making these changes. The value must be less than or
// 128 characters long, and only contain printable characters,
// as defined by https://golang.org/pkg/unicode/#IsPrint. This
// field is required for apply requests
// (application/apply-patch) but optional for non-apply patch
// types (JsonPatch, MergePatch, StrategicMergePatch).
// +optional
FieldManager string `json:"fieldManager,omitempty" protobuf:"bytes,3,name=fieldManager"`
}
// ApplyOptions may be provided when applying an API object.
// FieldManager is required for apply requests.
// ApplyOptions is equivalent to PatchOptions. It is provided as a convenience with documentation
// that speaks specifically to how the options fields relate to apply.
type ApplyOptions struct {
TypeMeta `json:",inline"`
// When present, indicates that modifications should not be
// persisted. An invalid or unrecognized dryRun directive will
// result in an error response and no further processing of the
// request. Valid values are:
// - All: all dry run stages will be processed
// +optional
DryRun []string `json:"dryRun,omitempty" protobuf:"bytes,1,rep,name=dryRun"`
// Force is going to "force" Apply requests. It means user will
// re-acquire conflicting fields owned by other people.
Force bool `json:"force" protobuf:"varint,2,opt,name=force"`
// fieldManager is a name associated with the actor or entity
// that is making these changes. The value must be less than or
// 128 characters long, and only contain printable characters,
// as defined by https://golang.org/pkg/unicode/#IsPrint. This
// field is required.
FieldManager string `json:"fieldManager" protobuf:"bytes,3,name=fieldManager"`
}
// k8s.io/apimachinery/pkg/types
//
package types
// Similarly to above, these are constants to support HTTP PATCH utilized by
// both the client and server that didn't make sense for a whole package to be
// dedicated to.
type PatchType string
const (
JSONPatchType PatchType = "application/json-patch+json"
MergePatchType PatchType = "application/merge-patch+json"
StrategicMergePatchType PatchType = "application/strategic-merge-patch+json"
ApplyPatchType PatchType = "application/apply-patch+yaml"
)
// https://github.com/kubernetes/code-generator/blob/master/tools.go
// tags: openapi, crd, code generation, protobuf
//go:build tools
// +build tools
// This package contains code generation utilities
// This package imports things required by build scripts, to force `go mod` to see them as dependencies
package tools
import (
_ "k8s.io/code-generator/cmd/client-gen"
_ "k8s.io/code-generator/cmd/conversion-gen"
_ "k8s.io/code-generator/cmd/deepcopy-gen"
_ "k8s.io/code-generator/cmd/defaulter-gen"
_ "k8s.io/code-generator/cmd/go-to-protobuf"
_ "k8s.io/code-generator/cmd/import-boss"
_ "k8s.io/code-generator/cmd/informer-gen"
_ "k8s.io/code-generator/cmd/lister-gen"
_ "k8s.io/code-generator/cmd/openapi-gen"
_ "k8s.io/code-generator/cmd/register-gen"
_ "k8s.io/code-generator/cmd/set-gen"
)
$ ls -ltr
drwxr-xr-x 4 amitd staff 128 Sep 9 18:14 checks
-rw-r--r-- 1 amitd staff 515 Sep 9 18:37 suite.go
-rw-r--r-- 1 amitd staff 964 Sep 10 11:43 setup.go
-rw-r--r-- 1 amitd staff 832 Sep 10 12:07 tools.go
-rw-r--r-- 1 amitd staff 327 Sep 10 12:07 go.mod
-rw-r--r-- 1 amitd staff 161109 Sep 10 12:07 go.sum
drwxr-xr-x 13 amitd staff 416 Sep 10 12:08 vendor
# below works only if above tools.go is present
$ go install k8s.io/code-generator/cmd/deepcopy-gen
$ which deepcopy-gen
/Users/amitd/go/bin/deepcopy-gen