{"id":21934069,"url":"https://github.com/jinghzhu/kubernetescrd","last_synced_at":"2025-10-19T06:48:34.987Z","repository":{"id":55841342,"uuid":"113328984","full_name":"jinghzhu/KubernetesCRD","owner":"jinghzhu","description":"Example of how to create and manage Kubernetes Custom Resource Definition.","archived":false,"fork":false,"pushed_at":"2020-12-11T13:48:58.000Z","size":30268,"stargazers_count":107,"open_issues_count":1,"forks_count":23,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-29T13:35:23.856Z","etag":null,"topics":["crd","go","k8s","kubernetes","register-crd"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jinghzhu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-12-06T14:51:22.000Z","updated_at":"2024-07-27T05:54:21.000Z","dependencies_parsed_at":"2022-08-15T07:40:56.492Z","dependency_job_id":null,"html_url":"https://github.com/jinghzhu/KubernetesCRD","commit_stats":null,"previous_names":["jinghzhu/k8scrd"],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jinghzhu%2FKubernetesCRD","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jinghzhu%2FKubernetesCRD/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jinghzhu%2FKubernetesCRD/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jinghzhu%2FKubernetesCRD/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jinghzhu","download_url":"https://codeload.github.com/jinghzhu/KubernetesCRD/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249819977,"owners_count":21329887,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["crd","go","k8s","kubernetes","register-crd"],"created_at":"2024-11-29T00:14:28.527Z","updated_at":"2025-10-19T06:48:29.959Z","avatar_url":"https://github.com/jinghzhu.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Kubernetes Custom Resource Definition\n\nThis repository is an example of how to create/list/update/delete Kubernetes [Custom Resource Definition](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/). If you also want to learn how to develop your own [Operator](https://coreos.com/operators/), please visit:\n* [CRD Operator](https://github.com/jinghzhu/KubernetesCRDOperator)\n* [Pod Operator](https://github.com/jinghzhu/KubernetesPodOperator)\n\nThe CRD example I write here is like the [ReplicaSet](https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/). You can use my CRD with my Operators mentioned before to run following scenarios:\n1. Register CRD.\n2. The Operator can automatically reconcile current state to desired state. By saying state, it means the number of running Pods.\n3. High availability guarantee, which means if you manually delete a Pod via kubectl, the Operator will automatically reconcile it.\n\n\n\n# Environment\n1. Go: \u003e= v1.9.0\n2. Kubernetes: \u003e= v1.18.0\n3. Assume you have already a Kubernetes cluster and its kubeconfig file can be reached via system variable `CRD_KUBECONFIG` and it will create a CRD instance in namespace `crd` by default, which you can also modify via system variable `CRD_NAMESPACE`. For more details, please check package `pkg/config`/\n\n\n\n# Dependency Package\nThe CRD is mainly developed in repository [apiextensions-apiserver](https://github.com/kubernetes/apiextensions-apiserver) which depends on [client-go](https://github.com/kubernetes/client-go) and [apimachinery](https://github.com/kubernetes/apimachinery).\n\nThis code is based on Kubernetes v1.18.12:\n\n* **k8s.io/client-go** with version `v0.18.12`.\n* **k8s.io/apimachinery** with version `v0.18.12`.\n* **k8s.io/apiextensions-apiserver** with version `v0.18.12`.\n\n\n\n# Step-by-step Instructions\n## Define CRD Object\nWe need firstly create the struct of CRD. The CRD object structure has these components:\n\n* `Metadata`\n\n    Standard Kubernetes properties like name, namespace, labels, etc.\n\n* `Spec`\n\n    CR configuration.\n\n    Each instance of our CR has an attached Spec, which should be defined via a `struct{}` to provide data format validation. In practice, this Spec is arbitrary key-value data that specifies the configuration/behavior of the CR.\n\n* `Status`\n\n    Used by the CR controller in response to Spec updates.\n\n```go\n// pkg/crd/jinghzhu/v1/types.go\n\n// Jinghzhu is the CRD. Use this command to generate deepcopy for it:\ntype Jinghzhu struct {\n\tmetav1.TypeMeta `json:\",inline\"`\n\t// Standard object's metadata.\n\tmetav1.ObjectMeta `json:\"metadata\"`\n\t// Specification of the desired behavior of Jinghzhu.\n\tSpec JinghzhuSpec `json:\"spec\"`\n\t// Observed status of Jinghzhu.\n\tStatus JinghzhuStatus `json:\"status\"`\n}\n\n// JinghzhuSpec is a desired state description of Jinghzhu.\ntype JinghzhuSpec struct {\n\t// Desired is the desired Pod number.\n\tDesired int `json:\"desired\"`\n\t// Current is the number of Pod currently running.\n\tCurrent int `json:\"current\"`\n\t// PodList is the name list of current Pods.\n\tPodList []string `json:\"podList\"`\n}\n\n// JinghzhuStatus describes the lifecycle status of Jinghzhu.\ntype JinghzhuStatus struct {\n\tState   string `json:\"state\"`\n\tMessage string `json:\"message\"`\n}\n```\n\nWe need to leverage the automatically code generated script to create the deep copy methods for CRD object. The result can be found at:\n* `pkg/crd/jinghzhu/v1/zz_generated.deepcopy.go`\n* `pkg/crd/jinghzhu/v1/apis`\n\nYou can get **code-generator** from [GitHub](https://github.com/kubernetes/code-generator).\n\nIn my example, I run following command to generate that file:\n```bash\n$ ./k8s.io/code-generator/generate-groups.sh all github.com/jinghzhu/KubernetesCRD/pkg/crd/jinghzhu/v1/apis github.com/jinghzhu/KubernetesCRD/pkg/crd \"jinghzhu:v1\"\n```\n\nPlease note that the code-generator requires annotation to work as expected. If you carefully read my code, you can find the annotations at:\n\n* `pkg/crd/jinghzhu/v1/types.go`:\n\n```go\n// +genclient\n// +genclient:noStatus\n// +k8s:deepcopy-gen=true\n// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object\n// +resource:path=jinghzhu\n\n...\n\n// +k8s:deepcopy-gen=true\n\n...\n\n// +k8s:deepcopy-gen=true\n// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object\n// +resource:path=jinghzhu\n```\n\n* `pkg/crd/jinghzhu/v1/doc.go`:\n\n```go\n// +k8s:deepcopy-gen=package,register\n// +k8s:defaulter-gen=TypeMeta\n// +k8s:openapi-gen=true\n\n// Package v1 is the v1 version of the API.\n// +groupName=jinghzhu.io\n```\n\nFor more details about annotations for code-generator, please read Kubernetes documents or Google it.\n\n\u003e If you use dep to manage dependency packages, you may not directly run the script, generate-groups. It will throw the error that it can't find necessary vendor packages.\n\u003e \n\u003e The solution is to deploy the dependency packages in `$GOPATH`. For more details, please view the [kubernetes/code-generator#21](https://github.com/kubernetes/code-generator/issues/21).\n\n\n## Register CRD\nThe CRD name (`CRDName string = Plural + \".\" + GroupName`) is the combination of CR plural (`Plural string = \"jinghzhus\"`) and CR group(`GroupName string = \"jinghzhu.io\"`) which can be used for the reference of Kubernetes CLI or API. CR group and version also define API endpoints.\n\n`pkg/crd/jinghzhu/v1/register.go`:\n\n```go\nconst (\n\t// Kind is normally the CamelCased singular type. The resource manifest uses this.\n\tKind string = \"Jinghzhu\"\n\t// GroupVersion is the version.\n\tGroupVersion string = \"v1\"\n\t// Plural is the plural name used in /apis/\u003cgroup\u003e/\u003cversion\u003e/\u003cplural\u003e\n\tPlural string = \"jinghzhus\"\n\t// Singular is used as an alias on kubectl for display.\n\tSingular string = \"jinghzhu\"\n\t// CRDName is the CRD name for Jinghzhu.\n\tCRDName string = Plural + \".\" + crdjinghzhu.GroupName\n\t// ShortName is the short alias for the CRD.\n\tShortName string = \"jh\"\n)\n```\n\nThe method, `CreateCustomResourceDefinition` at `pkg/crd/jinghzhu/v1/crd.go`, covers the logic to register CRD into Kubernetes. Please note that Kubernetes doesn't immediately register CRD. So, it is better to add logic to wait for the creation like shown in the code.\n\nMeanwhile, I also leverage OpenAPI v3 to perform validation check. You can find related logic at `CreateCustomResourceDefinition@pkg/crd/jinghzhu/v1`:\n\n```go\n      Validation: \u0026apiextensionsv1beta1.CustomResourceValidation{\n\t\t\t\tOpenAPIV3Schema: \u0026apiextensionsv1beta1.JSONSchemaProps{\n\t\t\t\t\tType: \"object\",\n\t\t\t\t\tProperties: map[string]apiextensionsv1beta1.JSONSchemaProps{\n\t\t\t\t\t\t\"spec\": {\n\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\tProperties: map[string]apiextensionsv1beta1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\"desired\": {Type: \"integer\", Format: \"int\"},\n\t\t\t\t\t\t\t\t\"current\": {Type: \"integer\", Format: \"int\"},\n\t\t\t\t\t\t\t\t\"podList\": {\n\t\t\t\t\t\t\t\t\tType: \"array\",\n\t\t\t\t\t\t\t\t\tItems: \u0026apiextensionsv1beta1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\tSchema: \u0026apiextensionsv1beta1.JSONSchemaProps{Type: \"string\"},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tRequired: []string{\"desired\"},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n```\n\n\n## CRD Client\nAfter creating CRD, we can access via CLI. For easily usage, we hope it can also be accessed via API. So I develop some methods to wrapper some codes for CRD **Create**, **Update**, **Delete**, **Get**, and **List**. You can view them at `pkg/crd/jinghzhu/v1/client/client.go`.\n\n\n\n# Main Logic to Use CRD\nHere, I'll go through the main code (`main.go`) to show the main logic of everything set before for us to use CRD.\n\n1. Connect to Kubernetes.\n\n    ```go\n    kubeConfigPath := os.Getenv(\"KUBECONFIG\")\n\n\t  // Use kubeconfig to create client config.\n\t  clientConfig, err := clientcmd.BuildConfigFromFlags(\"\", kubeconfigPath)\n\t  if err != nil {\n\t\t  panic(err)\n    }\n\n    apiextensionsClientSet, err := apiextensionsclient.NewForConfig(clientConfig)\n\t  if err != nil {\n\t\t  panic(err)\n\t  }\n    ```\n\n2. Register the CRD. Please note that it can only be accessed by CLI now as mentioned before.\n\n    ```go\n    // Init a CRD kind.\n\t  if _, err = crdjinghzhuv1.CreateCustomResourceDefinition(apiextensionsClientSet); err != nil {\n\t\t  panic(err)\n\t  }\n    ```\n\n3. Create the API client to help access the CRD.\n\n    ```go\n    // Create a CRD client interface for Jinghzhu v1.\n\t  crdClient, err := jinghzhuv1client.NewClient(ctx, kubeconfigPath, cfg.GetCRDNamespace())\n\t  if err != nil {\n\t\t  panic(err)\n\t  }\n    ```\n\n4. Declare a CR object for test.\n\n    ```go\n    instanceName := \"jinghzhu-example-\"\n\t  exampleInstance := \u0026crdjinghzhuv1.Jinghzhu{\n\t\t  ObjectMeta: metav1.ObjectMeta{\n\t\t\t  GenerateName: instanceName,\n\t\t  },\n\t\t  Spec: crdjinghzhuv1.JinghzhuSpec{\n\t\t\t  Desired: 1,\n\t\t\t  Current: 0,\n\t\t\t  PodList: make([]string, 0),\n\t\t  },\n\t\t  Status: crdjinghzhuv1.JinghzhuStatus{\n\t\t\t  State:   types.StatePending,\n\t\t\t  Message: \"Created but not processed yet\",\n\t\t  },\n\t  }\n    ```\n\n5. Use the API client created in step 3 to create new CR.\n\n    ```go\n    result, err := crdClient.CreateDefault(exampleInstance)\n\t  if err != nil \u0026\u0026 apierrors.IsAlreadyExists(err) {\n\t\t  fmt.Printf(\"ALREADY EXISTS: %#v\\n\", result)\n\t  } else if err != nil {\n\t\t  panic(err)\n\t  }\n\t  crdInstanceName := result.GetName()\n\t  fmt.Println(\"CREATED: \" + result.String())\n\n\t  // Wait until the CRD object is handled by controller and its status is changed to Processed.\n\t  err = crdClient.WaitForInstanceProcessed(crdInstanceName)\n\t  if err != nil {\n\t\t  panic(err)\n\t  }\n\t  fmt.Println(\"Processed \" + crdInstanceName)\n\n\t  // Get the list of CRs.\n\t  exampleList, err := crdClient.List(metav1.ListOptions{})\n\t  if err != nil {\n\t\t  panic(err)\n\t  }\n\t  fmt.Printf(\"LIST: %#v\\n\", exampleList)\n    ```\n\n\n\n## Result\nIf everything goes well, you should see logs like:\n\n```bash\n$ go run cmd/crd/main.go\nCRD Jinghzhu is created\nCREATED:        Name = jinghzhu-example-f7wgv\n        Resource Version = 2343534\n        Desired = 1\n        Current = 0\n        PodList =\n        State = Pending\n        Message = Created but not processed yet\n\nProcessed jinghzhu-example-f7wgv\nLIST: \u0026v1.JinghzhuList{TypeMeta:v1.TypeMeta{Kind:\"\", APIVersion:\"\"}, ListMeta:v1.ListMeta{SelfLink:\"/apis/jinghzhu.io/v1/namespaces/crd/jinghzhus\", ResourceVersion:\"2343539\", Continue:\"\", RemainingItemCount:(*int64)(nil)}, Items:[]v1.Jinghzhu{v1.Jinghzhu{TypeMeta:v1.TypeMeta{Kind:\"Jinghzhu\", APIVersion:\"jinghzhu.io/v1\"}, ObjectMeta:v1.ObjectMeta{Name:\"jinghzhu-example-f7wgv\", GenerateName:\"jinghzhu-example-\", Namespace:\"crd\", SelfLink:\"/apis/jinghzhu.io/v1/namespaces/crd/jinghzhus/jinghzhu-example-f7wgv\", UID:\"c9a14049-e255-45be-99f7-033a5ea87787\", ResourceVersion:\"2343534\", Generation:1, CreationTimestamp:v1.Time{Time:time.Time{wall:0x0, ext:63743288791, loc:(*time.Location)(0x1c802e0)}}, DeletionTimestamp:(*v1.Time)(nil), DeletionGracePeriodSeconds:(*int64)(nil), Labels:map[string]string(nil), Annotations:map[string]string(nil), OwnerReferences:[]v1.OwnerReference(nil), Finalizers:[]string(nil), ClusterName:\"\", ManagedFields:[]v1.ManagedFieldsEntry{v1.ManagedFieldsEntry{Manager:\"main\", Operation:\"Update\", APIVersion:\"jinghzhu.io/v1\", Time:(*v1.Time)(0xc00039d1c0), FieldsType:\"FieldsV1\", FieldsV1:(*v1.FieldsV1)(0xc00039d1a0)}}}, Spec:v1.JinghzhuSpec{Desired:1, Current:0, PodList:[]string{}}, Status:v1.JinghzhuStatus{State:\"Pending\", Message:\"Created but not processed yet\"}}}}\n```\n\nNow, let's check CRD.\n\n```bash\n$ kubectl get crd\nNAME                    CREATED AT\njinghzhus.jinghzhu.io   2020-12-11T13:06:25Z\n```\n\n```bash\n$ kubectl describe crd jinghzhus.jinghzhu.com\nName:         jinghzhus.jinghzhu.io\nNamespace:\nLabels:       \u003cnone\u003e\nAnnotations:  \u003cnone\u003e\nAPI Version:  apiextensions.k8s.io/v1\nKind:         CustomResourceDefinition\nMetadata:\n  Creation Timestamp:  2020-12-11T13:06:25Z\n  Generation:          1\n  Managed Fields:\n    API Version:  apiextensions.k8s.io/v1\n    Fields Type:  FieldsV1\n    fieldsV1:\n      f:status:\n        f:acceptedNames:\n          f:kind:\n          f:listKind:\n          f:plural:\n          f:shortNames:\n          f:singular:\n        f:conditions:\n    Manager:      kube-apiserver\n    Operation:    Update\n    Time:         2020-12-11T13:06:25Z\n    API Version:  apiextensions.k8s.io/v1beta1\n    Fields Type:  FieldsV1\n    fieldsV1:\n      f:spec:\n        f:conversion:\n          .:\n          f:strategy:\n        f:group:\n        f:names:\n          f:kind:\n          f:listKind:\n          f:plural:\n          f:shortNames:\n          f:singular:\n        f:preserveUnknownFields:\n        f:scope:\n        f:validation:\n          .:\n          f:openAPIV3Schema:\n            .:\n            f:properties:\n              .:\n              f:spec:\n                .:\n                f:properties:\n                  .:\n                  f:current:\n                    .:\n                    f:format:\n                    f:type:\n                  f:desired:\n                    .:\n                    f:format:\n                    f:type:\n                  f:podList:\n                    .:\n                    f:items:\n                    f:type:\n                f:required:\n                f:type:\n            f:type:\n        f:version:\n        f:versions:\n      f:status:\n        f:storedVersions:\n    Manager:         main\n    Operation:       Update\n    Time:            2020-12-11T13:06:25Z\n  Resource Version:  2343519\n  Self Link:         /apis/apiextensions.k8s.io/v1/customresourcedefinitions/jinghzhus.jinghzhu.io\n  UID:               1afe3057-4398-45f7-9200-90b879d52f6a\nSpec:\n  Conversion:\n    Strategy:  None\n  Group:       jinghzhu.io\n  Names:\n    Kind:       Jinghzhu\n    List Kind:  JinghzhuList\n    Plural:     jinghzhus\n    Short Names:\n      jh\n    Singular:               jinghzhu\n  Preserve Unknown Fields:  true\n  Scope:                    Namespaced\n  Versions:\n    Name:  v1\n    Schema:\n      openAPIV3Schema:\n        Properties:\n          Spec:\n            Properties:\n              Current:\n                Format:  int\n                Type:    integer\n              Desired:\n                Format:  int\n                Type:    integer\n              Pod List:\n                Items:\n                  Type:  string\n                Type:    array\n            Required:\n              desired\n            Type:  object\n        Type:      object\n    Served:        true\n    Storage:       true\nStatus:\n  Accepted Names:\n    Kind:       Jinghzhu\n    List Kind:  JinghzhuList\n    Plural:     jinghzhus\n    Short Names:\n      jh\n    Singular:  jinghzhu\n  Conditions:\n    Last Transition Time:  2020-12-11T13:06:25Z\n    Message:               no conflicts found\n    Reason:                NoConflicts\n    Status:                True\n    Type:                  NamesAccepted\n    Last Transition Time:  2020-12-11T13:06:25Z\n    Message:               the initial names have been accepted\n    Reason:                InitialNamesAccepted\n    Status:                True\n    Type:                  Established\n  Stored Versions:\n    v1\nEvents:  \u003cnone\u003e\n```\n\n```bash\n$ kubectl proxy\nStarting to serve on 127.0.0.1:8001\n\n$ curl -i 127.0.0.1:8001/apis/jinghzhu.io\nHTTP/1.1 200 OK\nCache-Control: no-cache, private\nContent-Length: 253\nContent-Type: application/json\nDate: Fri, 11 Dec 2020 13:11:34 GMT\n\n{\n  \"kind\": \"APIGroup\",\n  \"apiVersion\": \"v1\",\n  \"name\": \"jinghzhu.io\",\n  \"versions\": [\n    {\n      \"groupVersion\": \"jinghzhu.io/v1\",\n      \"version\": \"v1\"\n    }\n  ],\n  \"preferredVersion\": {\n    \"groupVersion\": \"jinghzhu.io/v1\",\n    \"version\": \"v1\"\n  }\n}\n```\n\n```bash\n$ kubectl -n crd get jh\nNAME                     AGE\njinghzhu-example-f7wgv   6m20s\n\n$  kubectl -n crd describe jh jinghzhu-example-f7wgv\nName:         jinghzhu-example-f7wgv\nNamespace:    crd\nLabels:       \u003cnone\u003e\nAnnotations:  \u003cnone\u003e\nAPI Version:  jinghzhu.io/v1\nKind:         Jinghzhu\nMetadata:\n  Creation Timestamp:  2020-12-11T13:06:31Z\n  Generate Name:       jinghzhu-example-\n  Generation:          1\n  Managed Fields:\n    API Version:  jinghzhu.io/v1\n    Fields Type:  FieldsV1\n    fieldsV1:\n      f:metadata:\n        f:generateName:\n      f:spec:\n        .:\n        f:current:\n        f:desired:\n        f:podList:\n      f:status:\n        .:\n        f:message:\n        f:state:\n    Manager:         main\n    Operation:       Update\n    Time:            2020-12-11T13:06:31Z\n  Resource Version:  2343534\n  Self Link:         /apis/jinghzhu.io/v1/namespaces/crd/jinghzhus/jinghzhu-example-f7wgv\n  UID:               c9a14049-e255-45be-99f7-033a5ea87787\nSpec:\n  Current:  0\n  Desired:  1\n  Pod List:\nStatus:\n  Message:  Created but not processed yet\n  State:    Pending\nEvents:     \u003cnone\u003e\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjinghzhu%2Fkubernetescrd","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjinghzhu%2Fkubernetescrd","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjinghzhu%2Fkubernetescrd/lists"}