package main import ( "flag" "fmt" "os" "path/filepath" "strings" "github.com/google/go-containerregistry/pkg/name" "sigs.k8s.io/kustomize/api/filters/imagetag" "sigs.k8s.io/kustomize/api/krusty" "sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/kyaml/kio" "edge-infra.dev/pkg/k8s/eyaml/fieldspecs" "edge-infra.dev/pkg/k8s/kustomize" ) var ( inKustomizationPath string outManifestsPath string ) func init() { flag.StringVar(&inKustomizationPath, "src-kustomization", "", "path to source kustomization.yaml") flag.StringVar(&outManifestsPath, "out-manifests", "", "path to generated manifests") } func main() { flag.Parse() imageFilters := []kio.Filter{} // The non-flag arguments are tuples, each containing the existing image reference // and the captured output from running the associated container_push target. for _, arg := range flag.Args() { // Read passed ref file and parse pair := strings.Split(arg, "=") oldRef := pair[0] pushRefPath := pair[1] parsedRef, err := parseRef(pushRefPath) if err != nil { fmt.Printf("%s\n", err) os.Exit(1) } // Grab necessary fields for filter creation and kustomization modification reg := parsedRef.Context().RegistryStr() repo := parsedRef.Context().RepositoryStr() digest := parsedRef.Identifier() newName := filepath.Join(reg, repo) f := imagetag.Filter{ ImageTag: types.Image{ Name: oldRef, NewName: newName, Digest: digest, }, FsSlice: fieldspecs.Images, } imageFilters = append(imageFilters, f) } // Kustomize the input manifests with krusty res, err := krustomize() if err != nil { fmt.Printf("%s\n", err) os.Exit(1) } // Apply all image filters to the ResMap and convert to yaml resyaml, err := filter(res, imageFilters) if err != nil { fmt.Printf("%s\n", err) os.Exit(1) } // Write krustomized manifests err = os.WriteFile(outManifestsPath, resyaml, 0644) if err != nil { fmt.Printf("failed to write output manifests: %s\n", err) os.Exit(1) } } // parseRef reads the ref file and leverages the name library to parse the read reference func parseRef(path string) (name.Reference, error) { pushRef, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("failed to read OCIPushInfo ref file: %w", err) } parsedRef, err := name.ParseReference(strings.TrimSpace(string(pushRef))) if err != nil { return nil, fmt.Errorf("failed to parse reference from ref file: %w", err) } return parsedRef, nil } // krustomize executes a krusty kustomization, returning the resulting resmap func krustomize() (resmap.ResMap, error) { // Create kustomize FS for krusty, rooting at cwd // When running in bazel, this is the build sandbox root cwd, err := os.Getwd() if err != nil { return nil, fmt.Errorf("failed to get current working dir: %w", err) } kfs := &kustomize.FS{FS: os.DirFS(cwd)} krustomizer := krusty.MakeKustomizer( &krusty.Options{ LoadRestrictions: types.LoadRestrictionsNone, PluginConfig: types.DisabledPluginConfig(), Reorder: krusty.ReorderOptionLegacy, }, ) // Run the krusty kustomizer based on the input kustomization.yaml's path resmap, err := krustomizer.Run(kfs, filepath.Dir(inKustomizationPath)) if err != nil { fmt.Printf("failed to krustomize: %s\n", err) os.Exit(1) } return resmap, nil } // filter applies all image filters to the resmap from krusty and converts the filtered resmap to yaml func filter(resmap resmap.ResMap, imageFilters []kio.Filter) ([]byte, error) { for _, fltr := range imageFilters { err := resmap.ApplyFilter(fltr) if err != nil { return nil, fmt.Errorf("resmap filter execution failed: %w", err) } } resyaml, err := resmap.AsYaml() if err != nil { return nil, fmt.Errorf("failed to convert resmap to yaml: %w", err) } return resyaml, nil }