AmitKumarDas / fun-with-programming

ABC - Always Be Coding
2 stars 2 forks source link

k8s code 0000 #85

Closed AmitKumarDas closed 2 years ago

AmitKumarDas commented 3 years ago
// tags: deepcode
// repo: https://github.com/mitchellh/hashstructure/blob/master/hashstructure.go
//
// tags: walk, visit, reflect, kind, elem, value, pointer, 
// tags: parse / traverse a structure
//
// reflect.Value.Kind() =~ reflect.Ptr / Interface /  Int / Bool / Uint / etc.

func (w *walker) visit(v reflect.Value, opts *visitOpts) (uint64, error) {
        t := reflect.TypeOf(0)

    // Loop since these can be wrapped in multiple layers of pointers
    // and interfaces.
    for {
        // If we have an interface, dereference it. We have to do this up
        // here because it might be a nil in there and the check below must
        // catch that.
        if v.Kind() == reflect.Interface {
            v = v.Elem()
            continue
        }

        if v.Kind() == reflect.Ptr {
                        // ZeroNil is flag determining if nil pointer 
                        // should be treated equal to a zero value of pointed type. 
            if w.zeronil {
                t = v.Type().Elem()
            }
            v = reflect.Indirect(v)
            continue
        }

        break
    }
  ...
}
AmitKumarDas commented 3 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")
}
AmitKumarDas commented 3 years ago
// 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
}
AmitKumarDas commented 3 years ago
// 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))
}
AmitKumarDas commented 3 years ago
// 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"`
}
AmitKumarDas commented 3 years ago
// 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"
)
AmitKumarDas commented 3 years ago
// 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