package pallet import ( "context" "flag" "os" "path" "strings" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" ktypes "sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/yaml" "edge-infra.dev/pkg/f8n/warehouse/capability" "edge-infra.dev/pkg/f8n/warehouse/cluster" "edge-infra.dev/pkg/f8n/warehouse/lift/pack/types" "edge-infra.dev/pkg/lib/cli/rags" "edge-infra.dev/pkg/lib/cli/sink" ) // TODO: should scaffold more complete file structure based on cluster providers // TODO: add capabilities flag // TODO: autodetect // TODO: dir of files // TODO: resolve pallet var ( initCmd *sink.Command ) const ( // palletFilename the file name for the pallet palletFilename = "pallet.yaml" // kustomizationFilename the file name for the kustomization kustomizationFilename = "kustomization.yaml" // defaultPath the default path that the pallet will be generated defaultPath = "config/pallets" // gcpProjectIDAnnotation gcp project id annotation gcpProjectIDAnnotation = "cnrm.cloud.google.com/project-id" // edgePlatformLabel platform component annotation edgePlatformLabel = "platform.edge.ncr.com/component" ) // TODO: update to use declarative flags + clean up func NewInitCmd() *sink.Command { var ( name string namespace string outputPath string providers cluster.Providers capabilities capability.Capabilities team string vendor string description string kustomization bool kustomizationResources string kustomizationComponents string ) fs := rags.New("lift pallet init", flag.ContinueOnError) fs.StringVar(&name, "name", "", "the name of the pallet") fs.StringVar(&namespace, "namespace", "", "the namespace of the pallet") fs.StringVar(&outputPath, "output", defaultPath, "the output location to create the pallet file") fs.Var(&providers, "cluster-providers", "comma separated list of cluster providers the Pallet will define variants "+ "for. accumulates values if provided multiple times") fs.Var(&capabilities, "runtime-capabilities", "comma separated list of runtime capabilities by package name the Pallet "+ "will integrate with accumulates values if provided multiple times.") fs.StringVar(&team, "team", "", "the team responsible for the pallet") fs.StringVar(&vendor, "vendor", "", "the pallet vendor") fs.StringVar(&description, "description", "", "the pallet description") fs.BoolVar(&kustomization, "create-kustomization", false, "should a companion kustomization file be created") fs.StringVar(&kustomizationResources, "resources", "", "the comma separated list of kustomization resources") fs.StringVar(&kustomizationComponents, "components", "", "the comma separated list of kustomization components") initCmd = &sink.Command{ Use: "init ", Short: "Scaffold the source for a Warehouse package", Flags: fs.Rags(), Exec: func(_ context.Context, _ sink.Run) error { resources := parseResources(kustomizationResources) components := parseResources(kustomizationComponents) pllet := &types.Pallet{ TypeMeta: v1.TypeMeta{ Kind: types.PalletKind, APIVersion: types.APIVersion, }, ObjectMeta: v1.ObjectMeta{ Name: name, }, PalletSpec: types.PalletSpec{ Capabilities: capabilities, Description: description, Providers: providers, Team: team, Vendor: vendor, }, } palletYAML, err := yaml.Marshal(pllet) if err != nil { return err } if _, err := os.Stat(path.Join(outputPath, name)); os.IsNotExist(err) { err := os.MkdirAll(path.Join(outputPath, name), 0755) if err != nil { return err } } if err := writeFile(outputPath, name, palletFilename, palletYAML); err != nil { return err } if kustomization { yaml, err := createKustomization(name, namespace, resources, components) if err != nil { return err } return writeFile(outputPath, name, kustomizationFilename, yaml) } return nil }, } return initCmd } // createKustomization generates the k8s kustomization file. func createKustomization(name, namespace string, resources, components []string) ([]byte, error) { kusto := &ktypes.Kustomization{ TypeMeta: ktypes.TypeMeta{ Kind: ktypes.KustomizationKind, APIVersion: ktypes.KustomizationVersion, }, // TODO: add --infra flag and only add this to the infra-specific kustomization CommonAnnotations: map[string]string{ gcpProjectIDAnnotation: "${gcp_project_id}", }, CommonLabels: map[string]string{ edgePlatformLabel: name, }, Namespace: namespace, // TODO: need to normalie path? Resources: resources, Components: components, } kustoYaml, err := yaml.Marshal(kusto) if err != nil { return nil, err } return kustoYaml, nil } // writeFile utility func to write file to provided path. func writeFile(outDir, name, fileName string, yamlData []byte) error { err := os.WriteFile(path.Join(outDir, name, fileName), yamlData, 0644) return err } // parseResources parses the comma separated string of resources and components. func parseResources(value string) []string { return strings.Split(value, ",") }