...

Source file src/edge-infra.dev/hack/build/rules/kustomize/main.go

Documentation: edge-infra.dev/hack/build/rules/kustomize

     1  package main
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  	"strings"
     9  
    10  	"github.com/google/go-containerregistry/pkg/name"
    11  	"sigs.k8s.io/kustomize/api/filters/imagetag"
    12  	"sigs.k8s.io/kustomize/api/krusty"
    13  	"sigs.k8s.io/kustomize/api/resmap"
    14  	"sigs.k8s.io/kustomize/api/types"
    15  	"sigs.k8s.io/kustomize/kyaml/kio"
    16  
    17  	"edge-infra.dev/pkg/k8s/eyaml/fieldspecs"
    18  	"edge-infra.dev/pkg/k8s/kustomize"
    19  )
    20  
    21  var (
    22  	inKustomizationPath string
    23  	outManifestsPath    string
    24  )
    25  
    26  func init() {
    27  	flag.StringVar(&inKustomizationPath, "src-kustomization", "", "path to source kustomization.yaml")
    28  	flag.StringVar(&outManifestsPath, "out-manifests", "", "path to generated manifests")
    29  }
    30  
    31  func main() {
    32  	flag.Parse()
    33  
    34  	imageFilters := []kio.Filter{}
    35  
    36  	// The non-flag arguments are tuples, each containing the existing image reference
    37  	// and the captured output from running the associated container_push target.
    38  	for _, arg := range flag.Args() {
    39  		// Read passed ref file and parse
    40  		pair := strings.Split(arg, "=")
    41  		oldRef := pair[0]
    42  		pushRefPath := pair[1]
    43  
    44  		parsedRef, err := parseRef(pushRefPath)
    45  		if err != nil {
    46  			fmt.Printf("%s\n", err)
    47  			os.Exit(1)
    48  		}
    49  
    50  		// Grab necessary fields for filter creation and kustomization modification
    51  		reg := parsedRef.Context().RegistryStr()
    52  		repo := parsedRef.Context().RepositoryStr()
    53  		digest := parsedRef.Identifier()
    54  
    55  		newName := filepath.Join(reg, repo)
    56  
    57  		f := imagetag.Filter{
    58  			ImageTag: types.Image{
    59  				Name:    oldRef,
    60  				NewName: newName,
    61  				Digest:  digest,
    62  			},
    63  			FsSlice: fieldspecs.Images,
    64  		}
    65  		imageFilters = append(imageFilters, f)
    66  	}
    67  
    68  	// Kustomize the input manifests with krusty
    69  	res, err := krustomize()
    70  	if err != nil {
    71  		fmt.Printf("%s\n", err)
    72  		os.Exit(1)
    73  	}
    74  
    75  	// Apply all image filters to the ResMap and convert to yaml
    76  	resyaml, err := filter(res, imageFilters)
    77  	if err != nil {
    78  		fmt.Printf("%s\n", err)
    79  		os.Exit(1)
    80  	}
    81  
    82  	// Write krustomized manifests
    83  	err = os.WriteFile(outManifestsPath, resyaml, 0644)
    84  	if err != nil {
    85  		fmt.Printf("failed to write output manifests: %s\n", err)
    86  		os.Exit(1)
    87  	}
    88  }
    89  
    90  // parseRef reads the ref file and leverages the name library to parse the read reference
    91  func parseRef(path string) (name.Reference, error) {
    92  	pushRef, err := os.ReadFile(path)
    93  	if err != nil {
    94  		return nil, fmt.Errorf("failed to read OCIPushInfo ref file: %w", err)
    95  	}
    96  
    97  	parsedRef, err := name.ParseReference(strings.TrimSpace(string(pushRef)))
    98  	if err != nil {
    99  		return nil, fmt.Errorf("failed to parse reference from ref file: %w", err)
   100  	}
   101  
   102  	return parsedRef, nil
   103  }
   104  
   105  // krustomize executes a krusty kustomization, returning the resulting resmap
   106  func krustomize() (resmap.ResMap, error) {
   107  	// Create kustomize FS for krusty, rooting at cwd
   108  	// When running in bazel, this is the build sandbox root
   109  	cwd, err := os.Getwd()
   110  	if err != nil {
   111  		return nil, fmt.Errorf("failed to get current working dir: %w", err)
   112  	}
   113  	kfs := &kustomize.FS{FS: os.DirFS(cwd)}
   114  
   115  	krustomizer := krusty.MakeKustomizer(
   116  		&krusty.Options{
   117  			LoadRestrictions: types.LoadRestrictionsNone,
   118  			PluginConfig:     types.DisabledPluginConfig(),
   119  			Reorder:          krusty.ReorderOptionLegacy,
   120  		},
   121  	)
   122  
   123  	// Run the krusty kustomizer based on the input kustomization.yaml's path
   124  	resmap, err := krustomizer.Run(kfs, filepath.Dir(inKustomizationPath))
   125  	if err != nil {
   126  		fmt.Printf("failed to krustomize: %s\n", err)
   127  		os.Exit(1)
   128  	}
   129  
   130  	return resmap, nil
   131  }
   132  
   133  // filter applies all image filters to the resmap from krusty and converts the filtered resmap to yaml
   134  func filter(resmap resmap.ResMap, imageFilters []kio.Filter) ([]byte, error) {
   135  	for _, fltr := range imageFilters {
   136  		err := resmap.ApplyFilter(fltr)
   137  		if err != nil {
   138  			return nil, fmt.Errorf("resmap filter execution failed: %w", err)
   139  		}
   140  	}
   141  
   142  	resyaml, err := resmap.AsYaml()
   143  	if err != nil {
   144  		return nil, fmt.Errorf("failed to convert resmap to yaml: %w", err)
   145  	}
   146  
   147  	return resyaml, nil
   148  }
   149  

View as plain text