diff --git a/pkg/utils/docker/push.go b/pkg/utils/docker/push.go index 1305fdf2..bd07b9b3 100644 --- a/pkg/utils/docker/push.go +++ b/pkg/utils/docker/push.go @@ -18,7 +18,6 @@ package docker import ( "bytes" - "strings" dockerlib "github.com/fsouza/go-dockerclient" "github.com/pkg/errors" @@ -53,11 +52,8 @@ func (c *Push) PushImage(image Image) error { credentials, err := dockerlib.NewAuthConfigurationsFromDockerCfg() if err != nil { log.Warn(errors.Wrap(err, "Unable to retrieve .docker/config.json authentication details. Check that 'docker login' works successfully on the command line.")) - } - - // Handle legacy docker registry address - if strings.Contains(image.Registry, "docker.io") { - image.Registry = "https://index.docker.io/v1/" + } else { + handleDockerRegistry(credentials) } // Find the authentication matched to registry @@ -65,7 +61,10 @@ func (c *Push) PushImage(image Image) error { if !ok { // Fallback to unauthenticated access in case if no auth credentials are retrieved log.Infof("Authentication credential of registry '%s' is not found. Will try push without authentication.", image.Registry) - auth = dockerlib.AuthConfiguration{} + // Header X-Registry-Auth is required + // Or API error (400): Bad parameters and missing X-Registry-Auth: EOF will throw + // Just to make not empty struct + auth = dockerlib.AuthConfiguration{Username: "docker"} } log.Debugf("Pushing image with options %+v", options) @@ -79,3 +78,18 @@ func (c *Push) PushImage(image Image) error { log.Infof("Successfully pushed image '%s' to registry '%s'", image.Name, image.Registry) return nil } + +// handleDockerRegistry adapt legacy docker registry address +// After docker login to docker.io, there must be https://index.docker.io/v1/ in config.json of authentication +// Reference: https://docs.docker.com/engine/api/v1.23/ +// > However (for legacy reasons) the “official” Docker, Inc. hosted registry +// > must be specified with both a “https://” prefix and a “/v1/” suffix +// > even though Docker will prefer to use the v2 registry API. +func handleDockerRegistry(auth *dockerlib.AuthConfigurations) { + const address = "docker.io" + const legacyAddress = "https://index.docker.io/v1/" + + if legacyAddressConfig, ok := auth.Configs[legacyAddress]; ok { + auth.Configs[address] = legacyAddressConfig + } +} diff --git a/script/test/cmd/tests_new.sh b/script/test/cmd/tests_new.sh index 014cdd9c..912a9814 100755 --- a/script/test/cmd/tests_new.sh +++ b/script/test/cmd/tests_new.sh @@ -98,8 +98,7 @@ convert::check_artifacts_generated "kompose --build local -f $relative_path conv ##### # Test the build config with push image -# Default behavior with push image disabled +# see tests_push_image.sh for local push test +# Should warn when push image disabled cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/buildconfig/docker-compose-build-no-image.yml -o $TEMP_DIR/output_file convert --build=local --push-image-registry=whatever" convert::expect_warning "$cmd" "Push image registry 'whatever' is specified but push image is disabled, skipping pushing to repository" -# TODO Push image to docker.io as default. Then verify push success -# TODO Push image to a private registry. Then verify push success diff --git a/script/test/cmd/tests_push_image.sh b/script/test/cmd/tests_push_image.sh new file mode 100755 index 00000000..d9d5cc4f --- /dev/null +++ b/script/test/cmd/tests_push_image.sh @@ -0,0 +1,80 @@ +#!/bin/bash + +# Copyright 2017 The Kubernetes Authors All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing pe#rmissions and +# limitations under the License. + +# Here are tests for pushing image on authentication and custom registry. +# These tests only work on local for authentication inconvenient. +# Prerequisites: +# * `docker.io` account and docker login successfully +# * custom registry which login as well. Or a local hosted registry. +# * `jq` installed + +# Variables +TEMP_DIR="/tmp/kompose" +mkdir -p $TEMP_DIR +mkdir -p "$TEMP_DIR/build" + +DOCKER_LOGIN_USER="lexcao" # TODO change this to your account for pushing to docker.io +COMPOSE_FILE="$TEMP_DIR/docker-compose-push.yml" +BUILD_FILE="$TEMP_DIR/build/Dockerfile" +CUSTOM_REGISTRY="localhost:5000" # TODO change this to your local registry + +# Custom compose file based on parameter +build_file_content="FROM busybox + RUN touch /test" +echo "$build_file_content" >> "$BUILD_FILE" + +compose_file_content="version: \"2\" + +services: + foo: + build: \"./build\" + image: docker.io/$DOCKER_LOGIN_USER/foobar" + +echo "$compose_file_content" >> "$COMPOSE_FILE" + +# Some helper functions +function get_docker_hub_tag() { + local image=$1 + local tag=$2 + curl "https://hub.docker.com/v2/repositories/$image/tags/$tag/" | jq +} + +function get_custom_registry_tag() { + local image=$1 + local tag=$2 + curl "http://$CUSTOM_REGISTRY/v2/$image/manifests/$tag" -I +} + +################################################################################### +cmd="kompose convert -f $COMPOSE_FILE -o $TEMP_DIR/output_file --build=local --push-image=true" + +printf "Push image without custom registry default to docker.io\n" +echo "executing cmd '$cmd'" +$cmd +printf "\nVerify push success...\n" +get_docker_hub_tag "$DOCKER_LOGIN_USER/foobar" "latest" + +####### +printf "\nPush image with custom registry\n" +cmd="$cmd --push-image-registry=$CUSTOM_REGISTRY" +echo "executing cmd '$cmd'" +$cmd +#kompose convert -f "$COMPOSE_FILE" -o "$TEMP_DIR/output_file" --build=local --push-image=true +printf "\nVerify push success...\n" +get_custom_registry_tag "$DOCKER_LOGIN_USER/foobar" "latest" + +# Clean resource +rm -rf $TEMP_DIR