github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/build/inject.go (about)

     1  package build
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/pkg/errors"
     7  	"k8s.io/apimachinery/pkg/types"
     8  
     9  	"github.com/tilt-dev/tilt/internal/container"
    10  	"github.com/tilt-dev/tilt/internal/dockerfile"
    11  	"github.com/tilt-dev/tilt/pkg/apis/core/v1alpha1"
    12  )
    13  
    14  // Derived from
    15  // https://github.com/moby/buildkit/blob/175e8415e38228dbb75e6b54efd2c8e9fc5b1cbf/util/archutil/detect.go#L15
    16  var validBuildkitArchSet = map[string]bool{
    17  	"amd64":    true,
    18  	"arm64":    true,
    19  	"riscv64":  true,
    20  	"ppc64le":  true,
    21  	"s390x":    true,
    22  	"386":      true,
    23  	"mips64le": true,
    24  	"mips64":   true,
    25  	"arm":      true,
    26  }
    27  
    28  // Create a new ImageTarget with the platform OS/Arch from the target cluster.
    29  func InjectClusterPlatform(spec v1alpha1.DockerImageSpec, cluster *v1alpha1.Cluster) v1alpha1.DockerImageSpec {
    30  	if spec.Platform != "" || cluster == nil {
    31  		return spec
    32  	}
    33  
    34  	// Eventually, it might make sense to read the supported platforms
    35  	// off the buildkit server and negotiate the right one, but for
    36  	// now we hard-code a whitelist.
    37  	targetArch := cluster.Status.Arch
    38  	if !validBuildkitArchSet[targetArch] {
    39  		return spec
    40  	}
    41  
    42  	if targetArch == "arm" {
    43  		// This is typically communicated with GOARM.
    44  		// For now, just assume arm/v7
    45  		targetArch = "arm/v7"
    46  	}
    47  
    48  	// Currently Tilt only supports linux containers.
    49  	// We don't even build windows-compatible docker contexts.
    50  	spec.Platform = fmt.Sprintf("linux/%s", targetArch)
    51  	return spec
    52  }
    53  
    54  // Create a new ImageTarget with the Dockerfiles rewritten with the injected images.
    55  func InjectImageDependencies(spec v1alpha1.DockerImageSpec, imageMaps map[types.NamespacedName]*v1alpha1.ImageMap) (v1alpha1.DockerImageSpec, error) {
    56  	if len(spec.ImageMaps) == 0 {
    57  		return spec, nil
    58  	}
    59  
    60  	df := dockerfile.Dockerfile(spec.DockerfileContents)
    61  	buildArgs := spec.Args
    62  
    63  	ast, err := dockerfile.ParseAST(df)
    64  	if err != nil {
    65  		return spec, errors.Wrap(err, "injectImageDependencies")
    66  	}
    67  
    68  	for _, dep := range spec.ImageMaps {
    69  		im, ok := imageMaps[types.NamespacedName{Name: dep}]
    70  		if !ok || im.Status.ImageFromLocal == "" {
    71  			return spec, fmt.Errorf("missing image dependency: %s", dep)
    72  		}
    73  
    74  		image := im.Status.ImageFromLocal
    75  		imageRef, err := container.ParseNamedTagged(image)
    76  		if err != nil {
    77  			return spec, errors.Wrap(err, "injectImageDependencies parse")
    78  		}
    79  
    80  		selector, err := container.SelectorFromImageMap(im.Spec)
    81  		if err != nil {
    82  			return spec, errors.Wrap(err, "injectImageDependencies selector")
    83  		}
    84  
    85  		modified, err := ast.InjectImageDigest(selector, imageRef, buildArgs)
    86  		if err != nil {
    87  			return spec, errors.Wrap(err, "injectImageDependencies inject")
    88  		} else if !modified {
    89  			return spec, fmt.Errorf("Could not inject image %q into Dockerfile of image %q", image, selector)
    90  		}
    91  	}
    92  
    93  	newDf, err := ast.Print()
    94  	if err != nil {
    95  		return spec, errors.Wrap(err, "injectImageDependencies")
    96  	}
    97  
    98  	spec.DockerfileContents = newDf.String()
    99  
   100  	return spec, nil
   101  }