diff --git a/hack/test-extended.sh b/hack/test-extended.sh index 6c710b64db74..d21c4d6829cd 100755 --- a/hack/test-extended.sh +++ b/hack/test-extended.sh @@ -38,9 +38,12 @@ export OPENSHIFT_ON_PANIC=crash cleanup() { set +e - server_pids=$(pgrep -P $(cat ${BASETMPDIR}/server.pid)) - kill $server_pids $(cat ${BASETMPDIR}/server.pid) ${ETCD_PID} - rm -rf ${ETCD_DIR} + pid=$(cat ${BASETMPDIR}/server.pid 2>/dev/null) + if [ ! -z "$pid" ]; then + server_pids=$(pgrep -P $pid) + kill $server_pids $(cat ${BASETMPDIR}/server.pid) ${ETCD_PID} + fi + rm -rf ${ETCD_DIR-} echo "[INFO] Cleanup complete" } @@ -75,7 +78,7 @@ start_server() { --signer-key="${CERT_DIR}/ca/key.key" \ --signer-serial="${CERT_DIR}/ca/serial.txt" - echo "[INFO] Starting OpenShift server" + echo "[INFO] Starting OpenShift ..." sudo env "PATH=${PATH}" openshift start \ --listen="https://0.0.0.0:${OS_MASTER_PORT}" \ --public-master="https://${OS_MASTER_ADDR}" \ @@ -92,16 +95,25 @@ start_server() { start_docker_registry() { mkdir -p ${BASETMPDIR}/.registry - echo "[INFO] Creating Router" + echo "[INFO] Creating Router ..." openshift ex router --create --credentials="${KUBECONFIG}" \ --images='openshift/origin-${component}:latest' &>/dev/null - echo "[INFO] Creating Docker Registry" + echo "[INFO] Creating Registry ..." openshift ex registry --create --credentials="${KUBECONFIG}" \ --mount-host="${BASETMPDIR}/.registry" \ --images='openshift/origin-${component}:latest' &>/dev/null } +push_to_registry() { + local image=$1 + local registry=$2 + echo "[INFO] Caching $image to $registry" + ( docker tag $image "${registry}/${image}" && \ + docker push "${registry}/${image}" \ + ) &>/dev/null +} + # Go to the top of the tree. cd "${OS_ROOT}" @@ -134,10 +146,22 @@ wait_for_url_timed "http://${REGISTRY_ADDR}" "" $((2*TIME_MIN)) # "409 - Image already exists" during the 'push' when the Build finishes. # This is because Docker Registry cannot handle parallel pushes. # See: https://github.com/docker/docker-registry/issues/537 -echo "[INFO] Pushing openshift/ruby-20-centos7 image to ${REGISTRY_ADDR}" -docker tag openshift/ruby-20-centos7 ${REGISTRY_ADDR}/openshift/ruby-20-centos7 -docker push ${REGISTRY_ADDR}/openshift/ruby-20-centos7 &>/dev/null +push_to_registry "openshift/ruby-20-centos7" $REGISTRY_ADDR +push_to_registry "openshift/origin-custom-docker-builder" $REGISTRY_ADDR + +export REGISTRY_ADDR + +[ ! -z "${DEBUG-}" ] && set +e # Run all extended tests cases -echo "[INFO] Starting extended tests" -OS_TEST_PACKAGE="test/extended" OS_TEST_TAGS="extended" OS_TEST_NAMESPACE="extended" ${OS_ROOT}/hack/test-integration.sh $@ +while true; do + echo "[INFO] Starting extended tests ..." + time OS_TEST_PACKAGE="test/extended" OS_TEST_TAGS="extended" OS_TEST_NAMESPACE="extended" ${OS_ROOT}/hack/test-integration.sh $@ + if [ ! -z "${DEBUG-}" ]; then + read -p "Do you want to re-run the test cases? " yn + case $yn in + [Nn]* ) exit;; + * ) echo "Please answer yes or no.";; + esac + fi +done diff --git a/test/extended/builds_test.go b/test/extended/builds_test.go index 19b5da1d595c..1548372ef43c 100644 --- a/test/extended/builds_test.go +++ b/test/extended/builds_test.go @@ -6,10 +6,13 @@ import ( "fmt" "io/ioutil" "net/http" + "os" "strings" "testing" + "github.com/GoogleCloudPlatform/kubernetes/pkg/fields" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" + "github.com/GoogleCloudPlatform/kubernetes/pkg/watch" buildapi "github.com/openshift/origin/pkg/build/api" testutil "github.com/openshift/origin/test/util" ) @@ -18,116 +21,72 @@ func init() { testutil.RequireServer() } -// TestSTIContextDirBuild excercises the scenario of having the 'contextDir' set to -// directory where the application sources resides inside the repository. -// The STI strategy is used for this build and this test succeed when the Build -// completes and the resulting image is used for a Pod that replies to HTTP -// request. -func TestSTIContextDirBuild(t *testing.T) { - namespace := testutil.RandomNamespace("contextdir") - fmt.Printf("Using '%s' namespace\n", namespace) - - build := testutil.GetBuildFixture("fixtures/contextdir-build.json") +// TestPushSecretName exercises one of the complex Build scenarios, where you +// first build a Docker image using Docker build strategy, which will later by +// consumed by Custom build strategy to verify that the 'PushSecretName' (Docker +// credentials) were successfully transported to the builder. The content of the +// Secret file is verified in the end. +func TestPushSecretName(t *testing.T) { + namespace := testutil.RandomNamespace("secret") client, _ := testutil.GetClusterAdminClient(testutil.KubeConfigPath()) + kclient, _ := testutil.GetClusterAdminKubeClient(testutil.KubeConfigPath()) repo := testutil.CreateSampleImageRepository(namespace) + if repo == nil { t.Fatal("Failed to create ImageRepository") } defer testutil.DeleteSampleImageRepository(repo, namespace) - // TODO: Tweak the selector to match the build name - watcher, err := client.Builds(namespace).Watch(labels.Everything(), labels.Everything(), "0") + // Create Secret with dockercfg + secret := testutil.GetSecretFixture("fixtures/test-secret.json") + // TODO: Why do I need to set namespace here? + secret.Namespace = namespace + _, err := kclient.Secrets(namespace).Create(secret) if err != nil { - t.Fatalf("Failed to create watcher: %v", err) - } - defer watcher.Stop() - - newBuild, err := client.Builds(namespace).Create(build) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - - for event := range watcher.ResultChan() { - build, ok := event.Object.(*buildapi.Build) - if !ok { - t.Fatalf("cannot convert input to Build") - } - - // Iterate over watcher's results and search for - // the build we just started. Also make sure that - // the build is running, complete, or has failed - if build.Name == newBuild.Name { - switch build.Status { - case buildapi.BuildStatusFailed, buildapi.BuildStatusError: - t.Fatalf("Unexpected build status: ", buildapi.BuildStatusFailed) - case buildapi.BuildStatusComplete: - err := testutil.VerifyImage(repo, namespace, validateContextDirImage) - if err != nil { - t.Fatalf("The build image failed validation: %v", err) - } - return - } - } - } -} - -// TestDockerStrategyBuild exercises the Docker strategy build. This test succeed when -// the Docker image is successfully built. -func TestDockerStrategyBuild(t *testing.T) { - namespace := testutil.RandomNamespace("docker") - fmt.Printf("Using '%s' namespace\n", namespace) - - build := testutil.GetBuildFixture("fixtures/docker-build.json") - client, _ := testutil.GetClusterAdminClient(testutil.KubeConfigPath()) - - repo := testutil.CreateSampleImageRepository(namespace) - if repo == nil { - t.Fatal("Failed to create ImageRepository") + t.Fatalf("Failed to create Secret: %v", err) } - defer testutil.DeleteSampleImageRepository(repo, namespace) - // TODO: Tweak the selector to match the build name - watcher, err := client.Builds(namespace).Watch(labels.Everything(), labels.Everything(), "0") + watcher, err := client.Builds(namespace).Watch(labels.Everything(), fields.Everything(), "0") if err != nil { t.Fatalf("Failed to create watcher: %v", err) } defer watcher.Stop() - newBuild, err := client.Builds(namespace).Create(build) + // First build the builder image (custom build builder) + dockerBuild := testutil.GetBuildFixture("fixtures/test-secret-build.json") + newDockerBuild, err := client.Builds(namespace).Create(dockerBuild) if err != nil { - t.Fatalf("Unexpected error: %v", err) + t.Fatalf("Unable to create Build %s: %v", dockerBuild.Name, err) } + waitForComplete(newDockerBuild, watcher, t) - for event := range watcher.ResultChan() { - build, ok := event.Object.(*buildapi.Build) - if !ok { - t.Fatalf("cannot convert input to Build") - } + // Now build the application image using custom build (run the previous image) + // Custom build will copy the dockercfg file into the application image. + customBuild := testutil.GetBuildFixture("fixtures/test-custom-build.json") + imageName := fmt.Sprintf("%s/%s/%s", os.Getenv("REGISTRY_ADDR"), namespace, repo.Name) + customBuild.Parameters.Strategy.CustomStrategy.Image = imageName + newCustomBuild, err := client.Builds(namespace).Create(customBuild) + if err != nil { + t.Fatalf("Unable to create Build %s: %v", dockerBuild.Name, err) + } + waitForComplete(newCustomBuild, watcher, t) - if build.Name == newBuild.Name { - switch build.Status { - case buildapi.BuildStatusFailed, buildapi.BuildStatusError: - t.Fatalf("Unexpected build status: ", buildapi.BuildStatusFailed) - case buildapi.BuildStatusComplete: - // If the Docker build strategy finishes with Complete, then this test - // succeeded - return - } - } + // Verify that the dockercfg file is there + if err := testutil.VerifyImage(repo, "application", namespace, validatePushSecret); err != nil { + t.Fatalf("Image verification failed: %v", err) } } -// TestSTIContextDirBuild exercises the scenario of having the '.sti/environment' -// file in the application sources that should set the defined environment -// variables for the resulting application. HTTP request is made to Pod that -// runs the output image and this HTTP request should reply the value of the -// TEST_VAR. +// TestSTIEnvironmentBuild exercises the scenario where you have .sti/environment +// file in your source code repository and you use STI build strategy. In that +// case the STI build should read that file and set all environment variables +// from that file to output image. func TestSTIEnvironmentBuild(t *testing.T) { namespace := testutil.RandomNamespace("stienv") fmt.Printf("Using '%s' namespace\n", namespace) - build := testutil.GetBuildFixture("fixtures/sti-env-build.json") + build := testutil.GetBuildFixture("fixtures/test-env-build.json") client, _ := testutil.GetClusterAdminClient(testutil.KubeConfigPath()) repo := testutil.CreateSampleImageRepository(namespace) @@ -137,7 +96,7 @@ func TestSTIEnvironmentBuild(t *testing.T) { defer testutil.DeleteSampleImageRepository(repo, namespace) // TODO: Tweak the selector to match the build name - watcher, err := client.Builds(namespace).Watch(labels.Everything(), labels.Everything(), "0") + watcher, err := client.Builds(namespace).Watch(labels.Everything(), fields.Everything(), "0") if err != nil { t.Fatalf("Failed to create watcher: %v", err) } @@ -148,27 +107,9 @@ func TestSTIEnvironmentBuild(t *testing.T) { t.Fatalf("Unexpected error: %v", err) } - for event := range watcher.ResultChan() { - build, ok := event.Object.(*buildapi.Build) - if !ok { - t.Fatalf("cannot convert input to Build") - } - - // Iterate over watcher's results and search for - // the build we just started. Also make sure that - // the build is running, complete, or has failed - if build.Name == newBuild.Name { - switch build.Status { - case buildapi.BuildStatusFailed, buildapi.BuildStatusError: - t.Fatalf("Unexpected build status: ", buildapi.BuildStatusFailed) - case buildapi.BuildStatusComplete: - err := testutil.VerifyImage(repo, namespace, validateSTIEnvironment) - if err != nil { - t.Fatalf("The build image failed validation: %v", err) - } - return - } - } + waitForComplete(newBuild, watcher, t) + if err := testutil.VerifyImage(repo, "", namespace, validateSTIEnvironment); err != nil { + t.Fatalf("The build image failed validation: %v", err) } } @@ -187,17 +128,39 @@ func validateSTIEnvironment(address string) error { return nil } -// validateContextDirImage verifies that the image with contextDir set can -// properly start and respond to HTTP requests -func validateContextDirImage(address string) error { - resp, err := http.Get("http://" + address) +// validatePushSecret verifies that the content of the sample dockercfg is +// properly returned from the Pod running the image that contains this file. +func validatePushSecret(address string) error { + expected := `{"https://registryhost/v1":{"auth":"secret","email":"john@doe.com"}}` + resp, err := http.Get("http://" + address + "/SECRET_FILE") if err != nil { return err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) - if strings.TrimSpace(string(body)) != "success" { - return fmt.Errorf("Expected 'success' got '%v'", body) + if strings.TrimSpace(string(body)) != expected { + return fmt.Errorf("Expected '%s' '%s'", expected, body) } return nil } + +// waitForComplete waits for the Build to finish +func waitForComplete(build *buildapi.Build, w watch.Interface, t *testing.T) { + for event := range w.ResultChan() { + eventBuild, ok := event.Object.(*buildapi.Build) + if !ok { + t.Fatalf("Cannot convert input to Build") + } + if build.Name != eventBuild.Name { + continue + } + switch eventBuild.Status { + case buildapi.BuildStatusFailed, buildapi.BuildStatusError: + t.Fatalf("Unexpected status for Build %s: ", eventBuild.Name, buildapi.BuildStatusFailed) + case buildapi.BuildStatusComplete: + return + default: + fmt.Printf("Build %s updated: %v\n", eventBuild.Name, eventBuild.Status) + } + } +} diff --git a/test/extended/fixtures/context-build-app/Gemfile b/test/extended/fixtures/context-build-app/Gemfile deleted file mode 100644 index e965cf0cc729..000000000000 --- a/test/extended/fixtures/context-build-app/Gemfile +++ /dev/null @@ -1,3 +0,0 @@ -source "https://rubygems.org" - -gem "rack" diff --git a/test/extended/fixtures/context-build-app/config.ru b/test/extended/fixtures/context-build-app/config.ru deleted file mode 100644 index 006addaf1bdf..000000000000 --- a/test/extended/fixtures/context-build-app/config.ru +++ /dev/null @@ -1 +0,0 @@ -run Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["success"]]} diff --git a/test/extended/fixtures/contextdir-build.json b/test/extended/fixtures/contextdir-build.json deleted file mode 100644 index 6f9bd4dae962..000000000000 --- a/test/extended/fixtures/contextdir-build.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "apiVersion": "v1beta1", - "kind": "Build", - "metadata": { - "name": "sample", - "labels": { - "name": "sample" - } - }, - "parameters": { - "output": { - "to": { - "name": "samplerepository" - } - }, - "source": { - "type": "Git", - "git": { - "uri": "https://github.com/openshift/origin" - }, - "contextDir": "test/extended/fixtures/context-build-app" - }, - "strategy": { - "type": "STI", - "stiStrategy": { - "image": "openshift/ruby-20-centos7" - } - } - } -} diff --git a/test/extended/fixtures/custom-secret-builder/Dockerfile b/test/extended/fixtures/custom-secret-builder/Dockerfile new file mode 100644 index 000000000000..fffe61a97a50 --- /dev/null +++ b/test/extended/fixtures/custom-secret-builder/Dockerfile @@ -0,0 +1,3 @@ +FROM openshift/origin-custom-docker-builder +# Override the default build script +ADD build.sh /tmp/build.sh diff --git a/test/extended/fixtures/custom-secret-builder/build.sh b/test/extended/fixtures/custom-secret-builder/build.sh new file mode 100755 index 000000000000..8ab6f3157008 --- /dev/null +++ b/test/extended/fixtures/custom-secret-builder/build.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +set -e +set -o pipefail + +if [ ! -e "${DOCKER_SOCKET}" ]; then + echo "Docker socket missing at ${DOCKER_SOCKET}" + exit 1 +fi + +SECRET_PATH=${PUSH_DOCKERCFG_PATH:-} + +if [ -z "${SECRET_PATH}" ]; then + echo "The dockercfg not found in /var/run/secrets/push" + exit 1 +fi + +if [ -n "${OUTPUT_IMAGE}" ]; then + TAG="${OUTPUT_REGISTRY}/${OUTPUT_IMAGE}" +fi + +mkdir -p /tmp/build && cd /tmp/build +cp -v $SECRET_PATH /tmp/build/dockercfg +chmod 0666 /tmp/build/dockercfg + +# This ruby app just output content of file referenced by the environment +# variable. For example FOO=/tmp/test and then GET /FOO returns content of +# /tmp/test +cat > config.ru <<- EOF +def readfile(name); File.read(ENV[name]) rescue "not found #{ENV[name]}"; end +run Proc.new { |env| + path = env['PATH_INFO'].gsub(/^\//,'') + [200, {"Content-Type" => "text/raw"}, [readfile(path)]] +} +EOF + +cat > Dockerfile <<- EOF +FROM openshift/ruby-20-centos7 +ENV SECRET_FILE /opt/openshift/src/dockercfg +COPY dockercfg ./ +COPY config.ru ./ +CMD /usr/local/sti/run +EOF + +docker build --rm -t "${TAG}" . +docker push "${TAG}" diff --git a/test/extended/fixtures/docker-build-app/Dockerfile b/test/extended/fixtures/docker-build-app/Dockerfile deleted file mode 100644 index 6877861e1104..000000000000 --- a/test/extended/fixtures/docker-build-app/Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM busybox - -ENTRYPOINT echo "success" diff --git a/test/extended/fixtures/test-custom-build.json b/test/extended/fixtures/test-custom-build.json new file mode 100644 index 000000000000..6d714f9db39d --- /dev/null +++ b/test/extended/fixtures/test-custom-build.json @@ -0,0 +1,26 @@ +{ + "apiVersion": "v1beta1", + "kind": "Build", + "metadata": { + "name": "testcustom", + "labels": { + "name": "testcustom" + } + }, + "parameters": { + "output": { + "to": { + "name": "test" + }, + "tag": "application", + "pushSecretName": "docker" + }, + "strategy": { + "type": "Custom", + "customStrategy": { + "exposeDockerSocket": true, + "image": "172.30.17.104:5000/foo/bar" + } + } + } +} diff --git a/test/extended/fixtures/sti-env-build.json b/test/extended/fixtures/test-env-build.json similarity index 85% rename from test/extended/fixtures/sti-env-build.json rename to test/extended/fixtures/test-env-build.json index b5c8dc0c4444..f2fd3b394a6e 100644 --- a/test/extended/fixtures/sti-env-build.json +++ b/test/extended/fixtures/test-env-build.json @@ -2,15 +2,15 @@ "apiVersion": "v1beta1", "kind": "Build", "metadata": { - "name": "sample", + "name": "test", "labels": { - "name": "sample" + "name": "test" } }, "parameters": { "output": { "to": { - "name": "samplerepository" + "name": "test" } }, "source": { diff --git a/test/extended/fixtures/sample-image-repository.json b/test/extended/fixtures/test-image-repository.json similarity index 72% rename from test/extended/fixtures/sample-image-repository.json rename to test/extended/fixtures/test-image-repository.json index 69496889e084..2e2fc63adc51 100644 --- a/test/extended/fixtures/sample-image-repository.json +++ b/test/extended/fixtures/test-image-repository.json @@ -2,6 +2,6 @@ "apiVersion": "v1beta1", "kind": "ImageRepository", "metadata": { - "name": "samplerepository" + "name": "test" } } diff --git a/test/extended/fixtures/docker-build.json b/test/extended/fixtures/test-secret-build.json similarity index 52% rename from test/extended/fixtures/docker-build.json rename to test/extended/fixtures/test-secret-build.json index 701f148a7f08..690e598bcf09 100644 --- a/test/extended/fixtures/docker-build.json +++ b/test/extended/fixtures/test-secret-build.json @@ -2,23 +2,24 @@ "apiVersion": "v1beta1", "kind": "Build", "metadata": { - "name": "sample", + "name": "testsecretbuilder", "labels": { - "name": "sample" + "name": "testsecretbuilder" } }, "parameters": { "output": { "to": { - "name": "samplerepository" + "name": "test" } }, "source": { "type": "Git", "git": { - "uri": "https://github.com/openshift/origin" + "uri": "https://github.com/mfojtik/origin", + "ref": "build_secret_extended" }, - "contextDir": "test/extended/fixtures/docker-build-app" + "contextDir": "test/extended/fixtures/custom-secret-builder" }, "strategy": { "type": "Docker" diff --git a/test/extended/fixtures/test-secret.json b/test/extended/fixtures/test-secret.json new file mode 100644 index 000000000000..ae28ee3ce873 --- /dev/null +++ b/test/extended/fixtures/test-secret.json @@ -0,0 +1,10 @@ +{ + "apiVersion": "v1beta3", + "kind": "Secret", + "metadata": { + "name": "docker" + }, + "data": { + "dockercfg": "eyJodHRwczovL3JlZ2lzdHJ5aG9zdC92MSI6eyJhdXRoIjoic2VjcmV0IiwiZW1haWwiOiJqb2huQGRvZS5jb20ifX0K" + } +} diff --git a/test/util/helpers.go b/test/util/helpers.go index 927eeca0cfe6..0936663a2739 100644 --- a/test/util/helpers.go +++ b/test/util/helpers.go @@ -4,6 +4,7 @@ import ( "fmt" "io/ioutil" + kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" buildapi "github.com/openshift/origin/pkg/build/api" imageapi "github.com/openshift/origin/pkg/image/api" @@ -12,13 +13,12 @@ import ( // CreateSampleImageRepository creates a ImageRepository in given namespace func CreateSampleImageRepository(namespace string) *imageapi.ImageRepository { var repo imageapi.ImageRepository - jsonData, err := ioutil.ReadFile("fixtures/sample-image-repository.json") + jsonData, err := ioutil.ReadFile("fixtures/test-image-repository.json") if err != nil { fmt.Printf("ERROR: Unable to read: %v", err) return nil } latest.Codec.DecodeInto(jsonData, &repo) - client, _ := GetClusterAdminClient(KubeConfigPath()) result, err := client.ImageRepositories(namespace).Create(&repo) if err != nil { @@ -46,3 +46,14 @@ func GetBuildFixture(filename string) *buildapi.Build { latest.Codec.DecodeInto(jsonData, &build) return &build } + +func GetSecretFixture(filename string) *kapi.Secret { + var secret kapi.Secret + jsonData, err := ioutil.ReadFile(filename) + if err != nil { + fmt.Printf("ERROR: Unable to read %s: %v", filename, err) + return nil + } + latest.Codec.DecodeInto(jsonData, &secret) + return &secret +} diff --git a/test/util/verify.go b/test/util/verify.go index c23e7b08dbf1..ac01d608bba4 100644 --- a/test/util/verify.go +++ b/test/util/verify.go @@ -4,9 +4,10 @@ import ( "fmt" "net" "strconv" - "time" kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/fields" + "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" kubeutil "github.com/GoogleCloudPlatform/kubernetes/pkg/util" imageapi "github.com/openshift/origin/pkg/image/api" ) @@ -14,8 +15,8 @@ import ( type ValidateFunc func(string) error // VerifyImage verifies if the latest image in given ImageRepository is valid -func VerifyImage(repo *imageapi.ImageRepository, ns string, validator ValidateFunc) error { - pod := CreatePodFromImage(repo, ns) +func VerifyImage(repo *imageapi.ImageRepository, tag, ns string, validator ValidateFunc) error { + pod := CreatePodFromImage(repo, tag, ns) if pod == nil { return fmt.Errorf("Unable to create Pod for %+v", repo.Status.DockerImageRepository) } @@ -41,79 +42,54 @@ func WaitForAddress(pod *kapi.Pod, service *kapi.Service, ns string) (string, er if err != nil { return "", err } - // TODO: There is no Pods.Watch() (yet) in OpenShift. The Watch() is already - // implemented in upstream, so uncomment this once that happens. - /* - podWatch, err := client.Pods(ns).Watch(labels.Everything(), labels.Everything(), "0") - if err != nil { - return "", fmt.Errorf("Unable to create watcher for Pod: %v", err) - } - defer podWatch.Stop() - - running := false - for event := range podWatch.ResultChan() { - currentPod, ok := event.Object.(*kapi.Pod) - if !ok { - return "", fmt.Errorf("Unable to convert event object to Pod") - } - if pod.Name == currentPod.Name { - switch pod.Status.Phase { - case kapi.PodFailed: - return "", fmt.Errorf("Pod failed to run") - case kapi.PodRunning: - running = true - } - } - if running == true { - break - } + watcher, err := client.Endpoints(ns).Watch(labels.Everything(), fields.Everything(), "0") + if err != nil { + return "", fmt.Errorf("Unexpected error: %v", err) + } + defer watcher.Stop() + for event := range watcher.ResultChan() { + eventEndpoint, ok := event.Object.(*kapi.Endpoints) + if !ok { + return "", fmt.Errorf("Unable to convert object %+v to Endpoints", eventEndpoint) } - fmt.Printf("The Pod %s is now running.", pod.Name) - */ - - // Now wait for the service to get the endpoint - // TODO: Endpoints() have no Watch in upstream, once they do, replace this - // code to use Watch() - for retries := 240; retries != 0; retries-- { - endpoints, err := client.Endpoints(ns).Get(service.Name) - if err != nil { - fmt.Printf("%v\n", err) - time.Sleep(1 * time.Second) + if eventEndpoint.Name != service.Name { continue } - if len(endpoints.Endpoints) == 0 { - fmt.Printf("Waiting for Service %s endpoints...\n", service.Name) - time.Sleep(1 * time.Second) + if len(eventEndpoint.Endpoints) == 0 { + fmt.Printf("Waiting for %s address\n", eventEndpoint.Name) continue } - for i := range endpoints.Endpoints { - ep := &endpoints.Endpoints[i] - addr := net.JoinHostPort(ep.IP, strconv.Itoa(ep.Port)) - fmt.Printf("The Service %s has endpoint: %s", pod.Name, addr) + for i := range eventEndpoint.Endpoints { + e := &eventEndpoint.Endpoints[i] + addr := net.JoinHostPort(e.IP, strconv.Itoa(e.Port)) + fmt.Printf("Discovered new %s endpoint: %s\n", service.Name, addr) return addr, nil } } - return "", fmt.Errorf("Service does not get any endpoints") } // CreatePodFromImage creates a Pod from the latest image available in the Image // Repository -func CreatePodFromImage(repo *imageapi.ImageRepository, ns string) *kapi.Pod { +func CreatePodFromImage(repo *imageapi.ImageRepository, tag, ns string) *kapi.Pod { client, err := GetClusterAdminKubeClient(KubeConfigPath()) if err != nil { return nil } + imageName := repo.Status.DockerImageRepository + if len(tag) > 0 { + imageName += ":" + tag + } pod := &kapi.Pod{ ObjectMeta: kapi.ObjectMeta{ - Name: ns + "pod", - Labels: map[string]string{"name": ns + "pod"}, + Name: ns, + Labels: map[string]string{"name": ns}, }, Spec: kapi.PodSpec{ Containers: []kapi.Container{ { Name: "sample", - Image: repo.Status.DockerImageRepository, + Image: imageName, }, }, RestartPolicy: kapi.RestartPolicyNever, @@ -138,7 +114,7 @@ func CreateServiceForPod(pod *kapi.Pod, ns string) *kapi.Service { Name: ns, }, Spec: kapi.ServiceSpec{ - Selector: map[string]string{"name": pod.Name}, + Selector: map[string]string{"name": ns}, TargetPort: kubeutil.IntOrString{Kind: kubeutil.IntstrInt, IntVal: 8080}, Port: 8080, },