pkg/cmdhelpers/addon_helpers.go (138 lines of code) (raw):

package cmdhelpers import ( "fmt" "path" "strings" "github.com/Azure/draft/pkg/config" "github.com/Azure/draft/pkg/consts" "github.com/Azure/draft/pkg/filematches" "github.com/Azure/draft/pkg/prompts" log "github.com/sirupsen/logrus" "helm.sh/helm/v3/pkg/chart/loader" "sigs.k8s.io/kustomize/api/filesys" "sigs.k8s.io/kustomize/api/krusty" "sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/kyaml/yaml" ) type referenceResource struct { Name string Path string } var ReferenceResources map[string][]referenceResource = map[string][]referenceResource{ "service": { {Name: "service-namespace", Path: "metadata.namespace"}, {Name: "service-name", Path: "metadata.name"}, {Name: "service-port", Path: "spec.ports.port"}, }, } func PromptAddonValues(dest string, addonConfig *config.DraftConfig) error { err := prompts.RunPromptsFromConfigWithSkips(addonConfig) if err != nil { return err } log.Debug("got user inputs") deployType, err := getDeployType(dest) if err != nil { return err } referenceMap, err := GetReferenceValueMap(dest, deployType) if err != nil { return err } log.Debug("got reference map") // merge maps for refName, refVal := range referenceMap { if strings.Contains(strings.ToLower(refName), "namespace") && refVal == "" { refVal = "default" //hack here to have explicit namespacing, probably a better way to do this } addonConfig.SetVariable(refName, refVal) } return nil } func GetAddonDestPath(dest string) (string, error) { deployType, err := getDeployType(dest) if err != nil { return "", err } return path.Join(dest, consts.DeploymentFilePaths[deployType]), err } func getDeployType(dest string) (string, error) { deploymentType, err := filematches.FindDraftDeploymentFiles(dest) log.Debugf("found deployment type: %s", deploymentType) return deploymentType, err } func GetReferenceValueMap(deployType, dest string) (map[string]string, error) { referenceMap := make(map[string]string) var err error for referenceName, referenceResources := range ReferenceResources { switch deployType { case "helm": if err = extractHelmValuesToMap(referenceName, dest, referenceResources, referenceMap); err != nil { return nil, err } case "kustomize": if err = extractKustomizeValuesToMap(referenceName, dest, referenceResources, referenceMap); err != nil { return nil, err } case "manifests": if err = extractManifestValuesToMap(referenceName, dest, referenceResources, referenceMap); err != nil { return nil, err } } } return referenceMap, err } // TODO: should consolidate all deployTypes into single interface to abstract the implementations func extractHelmValuesToMap(referenceName, dest string, references []referenceResource, referenceMap map[string]string) error { chart, err := loader.Load(dest + "/charts/") if err != nil { return err } for _, reference := range references { referenceMap[reference.Name] = strings.ReplaceAll( consts.HelmReferencePathMapping[referenceName][reference.Path], ".Config.GetVariableValue \"APPNAME\"", chart.Name()) } return nil } func extractKustomizeValuesToMap(referenceName, dest string, references []referenceResource, referenceMap map[string]string) error { kustomizer := krusty.MakeKustomizer(&krusty.Options{PluginConfig: &types.PluginConfig{}}) production, err := kustomizer.Run(filesys.FileSystemOrOnDisk{}, dest+"/overlays/production/") if err != nil { return err } rNodes := production.ToRNodeSlice() return extractNativeRefMap(rNodes, referenceName, references, referenceMap) } func extractManifestValuesToMap(referenceName, dest string, references []referenceResource, referenceMap map[string]string) error { serviceYaml, err := yaml.ReadFile(dest + "/manifests/service.yaml") if err != nil { return err } rNodes := make([]*yaml.RNode, 0) rNodes = append(rNodes, serviceYaml) return extractNativeRefMap(rNodes, referenceName, references, referenceMap) } func extractNativeRefMap(referenceNodes []*yaml.RNode, referenceName string, references []referenceResource, referenceMap map[string]string) error { for _, reference := range references { refStr := extractRef(referenceNodes, consts.RefPathLookups[referenceName][reference.Path]) if refStr == "" && strings.Contains(reference.Name, "namespace") { //hack for default namespace refStr = "default" } else if refStr == "" { return fmt.Errorf("referenceResource %s not found", reference.Name) } referenceMap[reference.Name] = refStr } return nil } func extractRef(rNodes []*yaml.RNode, lookupPath []string) string { for _, rNode := range rNodes { ref, err := rNode.Pipe(yaml.Lookup(lookupPath...)) if ref == nil || err != nil { continue } refYNode := ref.YNode() // Convert to go yaml.node to extract values without trailing newlines refStr := refYNode.Value log.Debugf("found ref: %s", refStr) return refStr } return "" }