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