github.com/argoproj/argo-cd/v3@v3.2.1/hack/gen-crd-spec/main.go (about) 1 package main 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "os" 8 "os/exec" 9 "strings" 10 11 "github.com/argoproj/argo-cd/v3/pkg/apis/application" 12 13 "github.com/argoproj/gitops-engine/pkg/utils/kube" 14 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 15 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 16 "sigs.k8s.io/yaml" 17 ) 18 19 var kindToCRDPath = map[string]string{ 20 application.ApplicationFullName: "manifests/crds/application-crd.yaml", 21 application.AppProjectFullName: "manifests/crds/appproject-crd.yaml", 22 application.ApplicationSetFullName: "manifests/crds/applicationset-crd.yaml", 23 } 24 25 func getCustomResourceDefinitions() map[string]*apiextensionsv1.CustomResourceDefinition { 26 crdYamlBytes, err := exec.Command( 27 "controller-gen", 28 "paths=./pkg/apis/application/...", 29 "crd:crdVersions=v1", 30 "output:crd:stdout", 31 ).Output() 32 checkErr(err) 33 34 // clean up stuff left by controller-gen 35 deleteFile("config/webhook/manifests.yaml") 36 deleteFile("config/webhook") 37 deleteFile("config/argoproj.io_applications.yaml") 38 deleteFile("config/argoproj.io_appprojects.yaml") 39 deleteFile("config/argoproj.io_applicationsets.yaml") 40 deleteFile("config") 41 42 objs, err := kube.SplitYAML(crdYamlBytes) 43 checkErr(err) 44 crds := make(map[string]*apiextensionsv1.CustomResourceDefinition) 45 for i := range objs { 46 un := objs[i] 47 48 // We need to completely remove validation of problematic fields such as creationTimestamp, 49 // which get marshalled to `null`, but are typed as as a `string` during Open API validation 50 removeValidation(un, "metadata.creationTimestamp") 51 // remove status validation for AppProject CRD as workaround for https://github.com/argoproj/argo-cd/issues/4158 52 if un.GetName() == "appprojects.argoproj.io" { 53 removeValidation(un, "status") 54 } 55 56 crd := toCRD(un, un.GetName() == "applicationsets.argoproj.io") 57 crd.Labels = map[string]string{ 58 "app.kubernetes.io/name": crd.Name, 59 "app.kubernetes.io/part-of": "argocd", 60 } 61 delete(crd.Annotations, "controller-gen.kubebuilder.io/version") 62 crd.Spec.Scope = "Namespaced" 63 crds[crd.Name] = crd 64 } 65 return crds 66 } 67 68 func deleteFile(path string) { 69 if _, err := os.Stat(path); os.IsNotExist(err) { 70 return 71 } 72 checkErr(os.Remove(path)) 73 } 74 75 func removeValidation(un *unstructured.Unstructured, path string) { 76 schemaPath := []string{"spec", "versions[*]", "schema", "openAPIV3Schema"} 77 for _, part := range strings.Split(path, ".") { 78 schemaPath = append(schemaPath, "properties", part) 79 } 80 unstructured.RemoveNestedField(un.Object, schemaPath...) 81 } 82 83 func toCRD(un *unstructured.Unstructured, removeDesc bool) *apiextensionsv1.CustomResourceDefinition { 84 if removeDesc { 85 removeDescription(un.Object) 86 } 87 unBytes, err := json.Marshal(un) 88 checkErr(err) 89 90 var crd apiextensionsv1.CustomResourceDefinition 91 err = json.Unmarshal(unBytes, &crd) 92 checkErr(err) 93 94 return &crd 95 } 96 97 func removeDescription(v any) { 98 switch v := v.(type) { 99 case []any: 100 for _, v := range v { 101 removeDescription(v) 102 } 103 case map[string]any: 104 if _, ok := v["description"]; ok { 105 _, ok := v["description"].(string) 106 if ok { 107 delete(v, "description") 108 } 109 } 110 for _, v := range v { 111 removeDescription(v) 112 } 113 } 114 } 115 116 func checkErr(err error) { 117 if err != nil { 118 var execError *exec.ExitError 119 if errors.As(err, &execError) { 120 fmt.Println(string(execError.Stderr)) 121 } 122 panic(err) 123 } 124 } 125 126 func main() { 127 crdsapp := getCustomResourceDefinitions() 128 for kind, path := range kindToCRDPath { 129 crd := crdsapp[kind] 130 if crd == nil { 131 panic(fmt.Sprintf("CRD of kind %s was not generated", kind)) 132 } 133 writeCRDintoFile(crd, path) 134 } 135 } 136 137 func writeCRDintoFile(crd *apiextensionsv1.CustomResourceDefinition, path string) { 138 jsonBytes, err := json.Marshal(crd) 139 checkErr(err) 140 141 var r unstructured.Unstructured 142 err = json.Unmarshal(jsonBytes, &r.Object) 143 checkErr(err) 144 145 // clean up crd yaml before marshalling 146 unstructured.RemoveNestedField(r.Object, "status") 147 unstructured.RemoveNestedField(r.Object, "metadata", "creationTimestamp") 148 jsonBytes, err = json.MarshalIndent(r.Object, "", " ") 149 checkErr(err) 150 151 yamlBytes, err := yaml.JSONToYAML(jsonBytes) 152 checkErr(err) 153 154 err = os.WriteFile(path, yamlBytes, 0o644) 155 checkErr(err) 156 }