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

     1  package container
     2  
     3  import (
     4  	"fmt"
     5  	"regexp"
     6  	"strings"
     7  
     8  	"github.com/distribution/reference"
     9  	"github.com/pkg/errors"
    10  
    11  	"github.com/tilt-dev/tilt/pkg/apis/core/v1alpha1"
    12  )
    13  
    14  var escapeRegex = regexp.MustCompile(`[/:@]`)
    15  
    16  func escapeName(s string) string {
    17  	return string(escapeRegex.ReplaceAll([]byte(s), []byte("_")))
    18  }
    19  
    20  // IsEmptyRegistry returns true if the object is nil or has no Host.
    21  func IsEmptyRegistry(reg *v1alpha1.RegistryHosting) bool {
    22  	if reg == nil || reg.Host == "" {
    23  		return true
    24  	}
    25  	return false
    26  }
    27  
    28  // RegistryFromCluster determines the registry that should be used for pushing
    29  // & pulling Tilt-built images.
    30  //
    31  // If the v1alpha1.Cluster object is not in a healthy state, an error is
    32  // returned.
    33  //
    34  // If the v1alpha1.Cluster object is healthy and provides local registry
    35  // metadata, that will be used.
    36  //
    37  // Otherwise, if the v1alpha1.Cluster object is healthy and does not provide
    38  // local registry metadata but a default registry for the cluster is defined
    39  // (typically via `default_registry` in the Tiltfile), the default registry
    40  // will be used.
    41  //
    42  // As a fallback, an empty registry will be returned, which indicates that _no_
    43  // registry rewriting should occur and Tilt should push and pull images to the
    44  // registry as specified by the configuration ref (e.g. what's passed in to
    45  // `docker_build` or `custom_build`).
    46  func RegistryFromCluster(cluster *v1alpha1.Cluster) (*v1alpha1.RegistryHosting, error) {
    47  	if cluster == nil {
    48  		return nil, nil
    49  	}
    50  
    51  	if cluster.Status.Error != "" {
    52  		// if the Cluster has not been initialized, we have not had a chance to
    53  		// read the local cluster info from it yet
    54  		return nil, fmt.Errorf("cluster not ready: %s", cluster.Status.Error)
    55  	}
    56  
    57  	if cluster.Status.Registry != nil {
    58  		return cluster.Status.Registry.DeepCopy(), nil
    59  	}
    60  
    61  	if cluster.Spec.DefaultRegistry != nil {
    62  		// no local registry is configured for this cluster, so use the default
    63  		return cluster.Spec.DefaultRegistry.DeepCopy(), nil
    64  	}
    65  
    66  	return nil, nil
    67  }
    68  
    69  // replaceRegistry produces a new image name that is in the specified registry.
    70  // The name might be ugly, favoring uniqueness and simplicity and assuming that the prettiness of ephemeral dev image
    71  // names is not that important.
    72  func replaceRegistry(defaultReg string, rs RefSelector, singleName string) (reference.Named, error) {
    73  	if defaultReg == "" {
    74  		return rs.AsNamedOnly(), nil
    75  	}
    76  
    77  	// Sometimes users get confused and put the local registry name in the YAML.
    78  	// No need to replace the registry in that case.
    79  	// https://github.com/tilt-dev/tilt/issues/4911
    80  	if strings.HasPrefix(rs.RefFamiliarName(), fmt.Sprintf("%s/", defaultReg)) {
    81  		return rs.AsNamedOnly(), nil
    82  	}
    83  
    84  	// validate the ref produced
    85  	newRefString := ""
    86  	if singleName == "" {
    87  		newRefString = fmt.Sprintf("%s/%s", defaultReg, escapeName(rs.RefFamiliarName()))
    88  	} else {
    89  		newRefString = fmt.Sprintf("%s/%s", defaultReg, singleName)
    90  	}
    91  
    92  	newRef, err := reference.ParseNamed(newRefString)
    93  	if err != nil {
    94  		return nil, errors.Wrapf(err, "Error parsing %s after applying default registry %s", newRefString, defaultReg)
    95  	}
    96  
    97  	return newRef, nil
    98  }
    99  
   100  // ReplaceRegistryForLocalRef returns a new reference using the target local
   101  // registry (as seen from the user).
   102  func ReplaceRegistryForLocalRef(rs RefSelector, reg *v1alpha1.RegistryHosting) (reference.Named, error) {
   103  	var host, singleName string
   104  	if !IsEmptyRegistry(reg) {
   105  		host = reg.Host
   106  		singleName = reg.SingleName
   107  	}
   108  	return replaceRegistry(host, rs, singleName)
   109  }
   110  
   111  // ReplaceRegistryForContainerRuntimeRef returns a new reference using the target local
   112  // registry (as seen from the container runtime).
   113  func ReplaceRegistryForContainerRuntimeRef(rs RefSelector, reg *v1alpha1.RegistryHosting) (reference.Named, error) {
   114  	var host, singleName string
   115  	if !IsEmptyRegistry(reg) {
   116  		host = reg.HostFromContainerRuntime
   117  		if host == "" {
   118  			host = reg.Host
   119  		}
   120  		singleName = reg.SingleName
   121  	}
   122  	return replaceRegistry(host, rs, singleName)
   123  }