Skip to content

Commit

Permalink
Merge pull request #590 from replicatedhq/divolgin/proxy
Browse files Browse the repository at this point in the history
Support proxying public images
  • Loading branch information
divolgin committed May 27, 2020
2 parents d208c23 + 47d5f52 commit 2642cce
Show file tree
Hide file tree
Showing 9 changed files with 89 additions and 26 deletions.
1 change: 1 addition & 0 deletions kotskinds/apis/kots/v1beta1/application_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type ApplicationSpec struct {
AdditionalImages []string `json:"additionalImages,omitempty"`
AdditionalNamespaces []string `json:"additionalNamespaces,omitempty"`
RequireMinimalRBACPrivileges bool `json:"requireMinimalRBACPrivileges,omitempty"`
ProxyPublicImages bool `json:"proxyPublicImages,omitempty"`
}

type ApplicationPort struct {
Expand Down
3 changes: 2 additions & 1 deletion pkg/base/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type FindPrivateImagesOptions struct {
AppSlug string
ReplicatedRegistry registry.RegistryOptions
Installation *kotsv1beta1.Installation
AllImagesPrivate bool
}

type FindPrivateImagesResult struct {
Expand All @@ -28,7 +29,7 @@ type FindPrivateImagesResult struct {

func FindPrivateImages(options FindPrivateImagesOptions) (*FindPrivateImagesResult, error) {
checkedImages := makeImageInfoMap(options.Installation.Spec.KnownImages)
upstreamImages, objects, err := image.GetPrivateImages(options.BaseDir, checkedImages)
upstreamImages, objects, err := image.GetPrivateImages(options.BaseDir, checkedImages, options.AllImagesPrivate)
if err != nil {
return nil, errors.Wrap(err, "failed to list upstream images")
}
Expand Down
8 changes: 7 additions & 1 deletion pkg/base/write_images.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,13 @@ func CopyUpstreamImages(options WriteUpstreamImageOptions) (*WriteUpstreamImageR
additionalImages = options.Application.Spec.AdditionalImages
}
checkedImages := makeImageInfoMap(options.Installation.Spec.KnownImages)
newImages, err := image.CopyImages(options.SourceRegistry, options.DestRegistry, options.AppSlug, options.Log, options.ReportWriter, options.BaseDir, additionalImages, options.DryRun, options.IsAirgap, checkedImages)

rewriteAll := options.IsAirgap
if options.Application != nil && options.Application.Spec.ProxyPublicImages {
rewriteAll = true
}

newImages, err := image.CopyImages(options.SourceRegistry, options.DestRegistry, options.AppSlug, options.Log, options.ReportWriter, options.BaseDir, additionalImages, options.DryRun, rewriteAll, checkedImages)
if err != nil {
return nil, errors.Wrap(err, "failed to save images")
}
Expand Down
7 changes: 3 additions & 4 deletions pkg/docker/registry/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,10 @@ func ProxyEndpointFromLicense(license *kotsv1beta1.License) *RegistryProxyInfo {
Registry: "registry.staging.replicated.com",
Proxy: "proxy.staging.replicated.com",
}
case "localhost":
// TODO: not real info
case "replicated-app":
return &RegistryProxyInfo{
Registry: "localhost:1234",
Proxy: "localhost:1234",
Registry: "registry:3000", // TODO: not real info
Proxy: "registry-proxy:3000",
}
default:
return defaultInfo
Expand Down
31 changes: 20 additions & 11 deletions pkg/image/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ type ImageInfo struct {
IsPrivate bool
}

func CopyImages(srcRegistry, destRegistry registry.RegistryOptions, appSlug string, log *logger.Logger, reportWriter io.Writer, upstreamDir string, additionalImages []string, dryRun, isAirgap bool, checkedImages map[string]ImageInfo) ([]kustomizeimage.Image, error) {
func CopyImages(srcRegistry, destRegistry registry.RegistryOptions, appSlug string, log *logger.Logger, reportWriter io.Writer, upstreamDir string, additionalImages []string, dryRun, allImagesPrivate bool, checkedImages map[string]ImageInfo) ([]kustomizeimage.Image, error) {
newImages := []kustomizeimage.Image{}

err := filepath.Walk(upstreamDir,
Expand All @@ -66,7 +66,7 @@ func CopyImages(srcRegistry, destRegistry registry.RegistryOptions, appSlug stri
return err
}

newImagesSubset, err := copyImagesInFileBetweenRegistries(srcRegistry, destRegistry, appSlug, log, reportWriter, contents, dryRun, isAirgap, checkedImages, newImages)
newImagesSubset, err := copyImagesInFileBetweenRegistries(srcRegistry, destRegistry, appSlug, log, reportWriter, contents, dryRun, allImagesPrivate, checkedImages, newImages)
if err != nil {
return errors.Wrapf(err, "failed to copy images mentioned in %s", path)
}
Expand All @@ -80,7 +80,7 @@ func CopyImages(srcRegistry, destRegistry registry.RegistryOptions, appSlug stri
}

for _, additionalImage := range additionalImages {
newImagesSubset, err := copyImageBetweenRegistries(srcRegistry, destRegistry, appSlug, log, reportWriter, additionalImage, dryRun, isAirgap, checkedImages)
newImagesSubset, err := copyImageBetweenRegistries(srcRegistry, destRegistry, appSlug, log, reportWriter, additionalImage, dryRun, allImagesPrivate, checkedImages)
if err != nil {
return nil, errors.Wrapf(err, "failed to addditional image: %s", additionalImage)
}
Expand All @@ -90,7 +90,7 @@ func CopyImages(srcRegistry, destRegistry registry.RegistryOptions, appSlug stri
return newImages, nil
}

func GetPrivateImages(upstreamDir string, checkedImages map[string]ImageInfo) ([]string, []*k8sdoc.Doc, error) {
func GetPrivateImages(upstreamDir string, checkedImages map[string]ImageInfo, allPrivate bool) ([]string, []*k8sdoc.Doc, error) {
uniqueImages := make(map[string]bool)

objects := make([]*k8sdoc.Doc, 0) // all objects where images are referenced from
Expand All @@ -113,6 +113,15 @@ func GetPrivateImages(upstreamDir string, checkedImages map[string]ImageInfo) ([
return listImagesInFile(contents, func(images []string, doc *k8sdoc.Doc) error {
numPrivateImages := 0
for _, image := range images {
if allPrivate {
checkedImages[image] = ImageInfo{
IsPrivate: true,
}
numPrivateImages = numPrivateImages + 1
uniqueImages[image] = true
continue
}

isPrivate := false
if i, ok := checkedImages[image]; ok {
isPrivate = i.IsPrivate
Expand Down Expand Up @@ -188,8 +197,8 @@ func GetObjectsWithImages(upstreamDir string) ([]*k8sdoc.Doc, error) {
return objects, nil
}

func copyImageBetweenRegistries(srcRegistry, destRegistry registry.RegistryOptions, appSlug string, log *logger.Logger, reportWriter io.Writer, imageName string, dryRun, isAirgap bool, checkedImages map[string]ImageInfo) ([]kustomizeimage.Image, error) {
newImage, err := copyOneImage(srcRegistry, destRegistry, imageName, appSlug, reportWriter, log, dryRun, isAirgap, checkedImages)
func copyImageBetweenRegistries(srcRegistry, destRegistry registry.RegistryOptions, appSlug string, log *logger.Logger, reportWriter io.Writer, imageName string, dryRun, allImagesPrivate bool, checkedImages map[string]ImageInfo) ([]kustomizeimage.Image, error) {
newImage, err := copyOneImage(srcRegistry, destRegistry, imageName, appSlug, reportWriter, log, dryRun, allImagesPrivate, checkedImages)
if err != nil {
log.FinishChildSpinner()
return nil, errors.Wrapf(err, "failed to transfer image %s", imageName)
Expand All @@ -198,7 +207,7 @@ func copyImageBetweenRegistries(srcRegistry, destRegistry registry.RegistryOptio
return newImage, nil
}

func copyImagesInFileBetweenRegistries(srcRegistry, destRegistry registry.RegistryOptions, appSlug string, log *logger.Logger, reportWriter io.Writer, fileData []byte, dryRun, isAirgap bool, checkedImages map[string]ImageInfo, alreadyPushedImagesFromOtherFiles []kustomizeimage.Image) ([]kustomizeimage.Image, error) {
func copyImagesInFileBetweenRegistries(srcRegistry, destRegistry registry.RegistryOptions, appSlug string, log *logger.Logger, reportWriter io.Writer, fileData []byte, dryRun, allImagesPrivate bool, checkedImages map[string]ImageInfo, alreadyPushedImagesFromOtherFiles []kustomizeimage.Image) ([]kustomizeimage.Image, error) {
savedImages := make(map[string]bool)
newImages := []kustomizeimage.Image{}

Expand All @@ -213,7 +222,7 @@ func copyImagesInFileBetweenRegistries(srcRegistry, destRegistry registry.Regist
}

log.ChildActionWithSpinner("Transferring image %s", image)
newImage, err := copyOneImage(srcRegistry, destRegistry, image, appSlug, reportWriter, log, dryRun, isAirgap, checkedImages)
newImage, err := copyOneImage(srcRegistry, destRegistry, image, appSlug, reportWriter, log, dryRun, allImagesPrivate, checkedImages)
if err != nil {
log.FinishChildSpinner()
return errors.Wrapf(err, "failed to transfer image %s", image)
Expand Down Expand Up @@ -268,7 +277,7 @@ func listImagesInFile(contents []byte, handler processImagesFunc) error {
return nil
}

func copyOneImage(srcRegistry, destRegistry registry.RegistryOptions, image string, appSlug string, reportWriter io.Writer, log *logger.Logger, dryRun, isAirgap bool, checkedImages map[string]ImageInfo) ([]kustomizeimage.Image, error) {
func copyOneImage(srcRegistry, destRegistry registry.RegistryOptions, image string, appSlug string, reportWriter io.Writer, log *logger.Logger, dryRun, allImagesPrivate bool, checkedImages map[string]ImageInfo) ([]kustomizeimage.Image, error) {
policy, err := signature.NewPolicyFromBytes(imagePolicy)
if err != nil {
return nil, errors.Wrap(err, "failed to read default policy")
Expand All @@ -286,11 +295,11 @@ func copyOneImage(srcRegistry, destRegistry registry.RegistryOptions, image stri
sourceCtx.DockerInsecureSkipTLSVerify = types.OptionalBoolTrue
}

isPrivate := isAirgap // rewrite all images with airgap
isPrivate := allImagesPrivate // rewrite all images with airgap
if i, ok := checkedImages[image]; ok {
isPrivate = i.IsPrivate
} else {
if !isAirgap {
if !allImagesPrivate {
p, err := IsPrivateImage(image)
if err != nil {
return nil, errors.Wrap(err, "failed to check if image is private")
Expand Down
8 changes: 7 additions & 1 deletion pkg/pull/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,11 @@ func Pull(upstreamURI string, pullOptions PullOptions) (string, error) {
return "", errors.Wrap(err, "failed to load installation")
}

application, err := upstream.LoadApplication(u.GetUpstreamDir(writeUpstreamOptions))
if err != nil {
return "", errors.Wrap(err, "failed to load application")
}

// Rewrite private images
findPrivateImagesOptions := base.FindPrivateImagesOptions{
BaseDir: writeBaseOptions.BaseDir,
Expand All @@ -410,7 +415,8 @@ func Pull(upstreamURI string, pullOptions PullOptions) (string, error) {
Endpoint: replicatedRegistryInfo.Registry,
ProxyEndpoint: replicatedRegistryInfo.Proxy,
},
Installation: newInstallation,
Installation: newInstallation,
AllImagesPrivate: application.Spec.ProxyPublicImages,
}
findResult, err := base.FindPrivateImages(findPrivateImagesOptions)
if err != nil {
Expand Down
8 changes: 7 additions & 1 deletion pkg/rewrite/rewrite.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,11 @@ func Rewrite(rewriteOptions RewriteOptions) error {
images = copyResult.Images
objects = affectedObjects
} else {
application, err := upstream.LoadApplication(u.GetUpstreamDir(writeUpstreamOptions))
if err != nil {
return errors.Wrap(err, "failed to load application")
}

// When CopyImages is not set, we only rewrite private images and use license to create secrets
// for all objects that have private images
findPrivateImagesOptions := base.FindPrivateImagesOptions{
Expand All @@ -236,7 +241,8 @@ func Rewrite(rewriteOptions RewriteOptions) error {
Endpoint: replicatedRegistryInfo.Registry,
ProxyEndpoint: replicatedRegistryInfo.Proxy,
},
Installation: rewriteOptions.Installation,
Installation: rewriteOptions.Installation,
AllImagesPrivate: application.Spec.ProxyPublicImages,
}
findResult, err := base.FindPrivateImages(findPrivateImagesOptions)
if err != nil {
Expand Down
17 changes: 10 additions & 7 deletions pkg/template/config_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ type ConfigCtx struct {
LocalRegistry LocalRegistry

license *kotsv1beta1.License // Another agument for unifying all these contexts
app *kotsv1beta1.Application
}

// newConfigContext creates and returns a context for template rendering
Expand Down Expand Up @@ -264,14 +265,16 @@ func (ctx ConfigCtx) localImageName(imageRef string) string {

// Not airgap and no local registry. Rewrite images that are private only.

isPrivate, err := image.IsPrivateImage(imageRef)
if err != nil {
// TODO: log
return ""
}
if ctx.app == nil || !ctx.app.Spec.ProxyPublicImages {
isPrivate, err := image.IsPrivateImage(imageRef)
if err != nil {
// TODO: log
return ""
}

if !isPrivate {
return imageRef
if !isPrivate {
return imageRef
}
}

proxyInfo := registry.ProxyEndpointFromLicense(ctx.license)
Expand Down
32 changes: 32 additions & 0 deletions pkg/template/config_context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,11 @@ func Test_localImageName(t *testing.T) {
Endpoint: "replicated.registry.com",
},
},
app: &kotsv1beta1.Application{
Spec: kotsv1beta1.ApplicationSpec{
ProxyPublicImages: false,
},
},
}

ctxWithoutRegistry := ConfigCtx{
Expand All @@ -401,6 +406,27 @@ func Test_localImageName(t *testing.T) {
Endpoint: "replicated.registry.com",
},
},
app: &kotsv1beta1.Application{
Spec: kotsv1beta1.ApplicationSpec{
ProxyPublicImages: false,
},
},
}

ctxWithoutRegistryProxyAll := ConfigCtx{
LocalRegistry: LocalRegistry{},

license: &kotsv1beta1.License{
Spec: kotsv1beta1.LicenseSpec{
AppSlug: "myslug",
Endpoint: "replicated.registry.com",
},
},
app: &kotsv1beta1.Application{
Spec: kotsv1beta1.ApplicationSpec{
ProxyPublicImages: true,
},
},
}

ctxWithNothing := ConfigCtx{
Expand Down Expand Up @@ -431,6 +457,12 @@ func Test_localImageName(t *testing.T) {
image: "redis:latest",
expected: "redis:latest",
},
{
name: "rewrite public image when ProxyPublicImages: true",
ctx: ctxWithoutRegistryProxyAll,
image: "redis:latest",
expected: "proxy.replicated.com/proxy/myslug/redis:latest",
},
{
name: "rewrite private image to proxy",
ctx: ctxWithoutRegistry,
Expand Down

0 comments on commit 2642cce

Please sign in to comment.