github.com/verrazzano/verrazzano@v1.7.0/tools/generate-profiles/generate.go (about) 1 // Copyright (c) 2022, Oracle and/or its affiliates. 2 // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 package main 4 5 import ( 6 "encoding/json" 7 "flag" 8 "fmt" 9 v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 "log" 11 "os" 12 "path/filepath" 13 "sigs.k8s.io/yaml" 14 15 "github.com/verrazzano/verrazzano/pkg/profiles" 16 "github.com/verrazzano/verrazzano/platform-operator/apis/verrazzano/v1beta1" 17 ) 18 19 var ( 20 profileType string 21 outputLocation string 22 verrazzanoDir string 23 help bool 24 ) 25 26 const ( 27 profileDirSuffix = "/platform-operator/manifests/profiles" 28 baseProfile = "base" 29 VzRootDir = "VERRAZZANO_ROOT" 30 ) 31 const defautlVerrazzano = `apiVersion: install.verrazzano.io/v1beta1 32 kind: Verrazzano 33 metadata: 34 name: verrazzano 35 namespace: default` 36 37 const info = `Utility tool to generate the standard Verrazzano profile files: prod, dev and managed cluster 38 39 Options: 40 --output-dir The output directory where the generated profile files will be saved. Defaults to current working directory. 41 --profile The type of profile file to be generated. Defaults to prod. 42 --help Get info about utility and usage. 43 44 Example: 45 export VERRAZZANO_ROOT=<local-verrazzano-repo-path> 46 go run ${VERRAZZANO_ROOT}/tools/generate-profiles/generate.go --profile dev --output-dir ${HOME} 47 ` 48 49 // main sets up args and calls the helper funcs 50 func main() { 51 defaultDir, err := os.Getwd() 52 if err != nil { 53 log.Fatal(err) 54 } 55 56 parseFlags(defaultDir) 57 if help { 58 fmt.Print(info) 59 os.Exit(0) 60 } 61 62 err = run(profileType, outputLocation) 63 if err != nil { 64 log.Fatal(err) 65 } 66 } 67 68 // run checks that VERRAZZANO_ROOT env var is set and the output location is valid 69 // and then generates the profile files at the desired location 70 func run(profileType string, outputLocation string) error { 71 verrazzanoDir = os.Getenv(VzRootDir) 72 if len(verrazzanoDir) == 0 { 73 return fmt.Errorf("VERRAZZANO_ROOT environment variable not specified") 74 } 75 76 // Validate the output location 77 OLInfo, err := os.Stat(outputLocation) 78 if err != nil { 79 return err 80 } 81 if !OLInfo.IsDir() { 82 return fmt.Errorf("Invalid parameter to specify directory: %s", outputLocation) 83 } 84 err = generateAndWrite(profileType, outputLocation, verrazzanoDir) 85 if err != nil { 86 return err 87 } 88 89 return nil 90 } 91 92 // generateAndWrite gets the merged Verrazzano CR and writes it to a file 93 func generateAndWrite(profileType string, outputLocation string, verrazzanoDir string) error { 94 cr, err := generateProfile(profileType, verrazzanoDir) 95 if err != nil { 96 return err 97 } 98 crYAML, err := yaml.Marshal(cr) 99 if err != nil { 100 return err 101 } 102 fileLoc := filepath.Join(outputLocation, profileType+".yaml") 103 file, err := os.Create(fileLoc) 104 if err != nil { 105 return err 106 } 107 defer file.Close() 108 err = os.WriteFile(fileLoc, crYAML, 0600) 109 if err != nil { 110 return err 111 } 112 return nil 113 } 114 115 // generateProfile executes the actual logic to merge the profiles 116 func generateProfile(profileType string, verrazzanoDir string) (*CRWrapper, error) { 117 cr := &v1beta1.Verrazzano{} 118 err := yaml.Unmarshal([]byte(defautlVerrazzano), cr) 119 if err != nil { 120 return nil, err 121 } 122 cr.Spec.Profile = v1beta1.ProfileType(profileType) 123 var profileFiles []string 124 profileFiles = append(profileFiles, profileFilePath(verrazzanoDir, cr, baseProfile)) 125 profileFiles = append(profileFiles, profileFilePath(verrazzanoDir, cr, profileType)) 126 // The profile type validation is handled here. All the profile template files are 127 // present at one location inside the platform-operator dir. If the given profile 128 // type is not valid, then an error is returned because of the absence of a YAML file 129 // for the given profile. 130 mergedCR, err := profiles.MergeProfilesForV1beta1(cr, profileFiles...) 131 if err != nil { 132 return nil, err 133 } 134 wrappedCR := CRWrapper(*mergedCR) 135 return &wrappedCR, nil 136 } 137 138 func profileFilePath(verrazzanoDir string, cr *v1beta1.Verrazzano, profileType string) string { 139 return filepath.Join(profileFilesDir(verrazzanoDir)+"/"+cr.GroupVersionKind().GroupVersion().Version, profileType+".yaml") 140 } 141 142 func profileFilesDir(verrazzanoDir string) string { 143 return filepath.Join(verrazzanoDir, profileDirSuffix) 144 } 145 146 func parseFlags(defaultDir string) { 147 flag.StringVar(&outputLocation, "output-dir", defaultDir, "The directory path where the profile artifact will be generated, defaults to current working directory") 148 flag.StringVar(&profileType, "profile", string(v1beta1.Prod), "Profile type to be generated, defaults to prod") 149 flag.BoolVar(&help, "help", false, "Get information about the usage/utility of the tool") 150 flag.Parse() 151 } 152 153 // Workaround to remove cruft. Certain empty fields that aren't required in the profile file (status, metadata.creationTimestamp etc) 154 // were showing up because Go's json package's (used by k8s yaml package) default behaviour is to Marshal fields of struct type 155 // even if they're empty. 156 157 // Alias to extend custom Marshalling 158 type CRWrapper v1beta1.Verrazzano 159 160 type PseudoObjectMeta struct { 161 Name string `json:"name,omitempty"` 162 Namespace string `json:"namespace,omitempty"` 163 } 164 165 func (cr *CRWrapper) MarshalJSON() ([]byte, error) { 166 return json.Marshal(&struct { 167 v1.TypeMeta `json:",inline"` 168 PseudoObjectMeta `json:"metadata,omitempty"` 169 Spec v1beta1.VerrazzanoSpec `json:"spec,omitempty"` 170 }{ 171 v1.TypeMeta{ 172 Kind: cr.Kind, 173 APIVersion: cr.APIVersion, 174 }, 175 PseudoObjectMeta{ 176 Name: cr.Name, 177 Namespace: cr.Namespace, 178 }, 179 cr.Spec, 180 }) 181 }