diff --git a/images/cli/Dockerfile b/images/cli/Dockerfile index 24e4a86bc959..d6d5eaf32387 100644 --- a/images/cli/Dockerfile +++ b/images/cli/Dockerfile @@ -10,7 +10,9 @@ RUN INSTALL_PKGS="origin-clients" && \ yum --enablerepo=origin-local-release install -y ${INSTALL_PKGS} && \ rpm -V ${INSTALL_PKGS} && \ yum clean all +COPY manifests /manifests LABEL io.k8s.display-name="OpenShift Client" \ io.k8s.description="OpenShift is a platform for developing, building, and deploying containerized applications." \ - io.openshift.tags="openshift,cli" + io.openshift.tags="openshift,cli" \ + io.openshift.release.operator="true" diff --git a/images/cli/manifests/01_imagestream.yaml b/images/cli/manifests/01_imagestream.yaml new file mode 100644 index 000000000000..0eb62af65f39 --- /dev/null +++ b/images/cli/manifests/01_imagestream.yaml @@ -0,0 +1,11 @@ +kind: ImageStream +apiVersion: image.openshift.io/v1 +metadata: + namespace: openshift + name: cli +spec: + tags: + - name: latest + from: + kind: DockerImage + name: docker.io/openshift/origin-cli:v4.0 diff --git a/images/cli/manifests/image-references b/images/cli/manifests/image-references new file mode 100644 index 000000000000..297f90912d5b --- /dev/null +++ b/images/cli/manifests/image-references @@ -0,0 +1,8 @@ +kind: ImageStream +apiVersion: image.openshift.io/v1 +spec: + tags: + - name: cli + from: + kind: DockerImage + name: docker.io/openshift/origin-cli:v4.0 diff --git a/test/extended/images/append.go b/test/extended/images/append.go index 2c858504f0cc..b9372e793056 100644 --- a/test/extended/images/append.go +++ b/test/extended/images/append.go @@ -22,8 +22,7 @@ func cliPodWithPullSecret(cli *exutil.CLI, shell string) *kapiv1.Pod { o.Expect(sa.ImagePullSecrets).NotTo(o.BeEmpty()) pullSecretName := sa.ImagePullSecrets[0].Name - format, _ := exutil.FindImageFormatString(cli) - cliImage := strings.Replace(format, "${component}", "cli", -1) + cliImage, _ := exutil.FindCLIImage(cli) return &kapiv1.Pod{ ObjectMeta: metav1.ObjectMeta{ diff --git a/test/extended/router/config_manager.go b/test/extended/router/config_manager.go index 62f319386ea8..becdd7399c18 100644 --- a/test/extended/router/config_manager.go +++ b/test/extended/router/config_manager.go @@ -43,7 +43,7 @@ var _ = g.Describe("[Conformance][Area:Networking][Feature:Router]", func() { g.BeforeEach(func() { ns = oc.Namespace() - routerImage, _ := exutil.FindImageFormatString(oc) + routerImage, _ := exutil.FindRouterImage(oc) routerImage = strings.Replace(routerImage, "${component}", "haproxy-router", -1) err := oc.AsAdmin().Run("new-app").Args("-f", configPath, "-p", "IMAGE="+routerImage).Execute() diff --git a/test/extended/router/scoped.go b/test/extended/router/scoped.go index 3c3753ef8db2..1c4e1ae9ef3b 100644 --- a/test/extended/router/scoped.go +++ b/test/extended/router/scoped.go @@ -50,7 +50,7 @@ var _ = g.Describe("[Conformance][Area:Networking][Feature:Router]", func() { g.BeforeEach(func() { ns = oc.Namespace() - routerImage, _ = exutil.FindImageFormatString(oc) + routerImage, _ = exutil.FindRouterImage(oc) routerImage = strings.Replace(routerImage, "${component}", "haproxy-router", -1) configPath := exutil.FixturePath("testdata", "router-common.yaml") diff --git a/test/extended/router/stress.go b/test/extended/router/stress.go index d1377c6ec412..495d7b9f630e 100644 --- a/test/extended/router/stress.go +++ b/test/extended/router/stress.go @@ -50,7 +50,7 @@ var _ = g.Describe("[Conformance][Area:Networking][Feature:Router]", func() { g.BeforeEach(func() { ns = oc.Namespace() - routerImage, _ = exutil.FindImageFormatString(oc) + routerImage, _ = exutil.FindRouterImage(oc) routerImage = strings.Replace(routerImage, "${component}", "haproxy-router", -1) _, err := oc.AdminKubeClient().Rbac().RoleBindings(ns).Create(&rbacv1.RoleBinding{ diff --git a/test/extended/router/unprivileged.go b/test/extended/router/unprivileged.go index ce7a326c94fa..7175ce4fbe1c 100644 --- a/test/extended/router/unprivileged.go +++ b/test/extended/router/unprivileged.go @@ -41,7 +41,7 @@ var _ = g.Describe("[Conformance][Area:Networking][Feature:Router]", func() { g.BeforeEach(func() { ns = oc.Namespace() - routerImage, _ = exutil.FindImageFormatString(oc) + routerImage, _ = exutil.FindRouterImage(oc) routerImage = strings.Replace(routerImage, "${component}", "haproxy-router", -1) configPath := exutil.FixturePath("testdata", "router-common.yaml") diff --git a/test/extended/router/weighted.go b/test/extended/router/weighted.go index 049bcf793379..7ca56814de24 100644 --- a/test/extended/router/weighted.go +++ b/test/extended/router/weighted.go @@ -26,7 +26,7 @@ var _ = g.Describe("[Conformance][Area:Networking][Feature:Router]", func() { ) g.BeforeEach(func() { - routerImage, _ := exutil.FindImageFormatString(oc) + routerImage, _ := exutil.FindRouterImage(oc) routerImage = strings.Replace(routerImage, "${component}", "haproxy-router", -1) err := oc.AsAdmin().Run("new-app").Args("-f", configPath, "-p", "IMAGE="+routerImage).Execute() o.Expect(err).NotTo(o.HaveOccurred()) diff --git a/test/extended/util/framework.go b/test/extended/util/framework.go index 57b5e369f39b..da6da4e79658 100644 --- a/test/extended/util/framework.go +++ b/test/extended/util/framework.go @@ -1489,16 +1489,42 @@ func GetRouterPodTemplate(oc *CLI) (*corev1.PodTemplateSpec, string, error) { return nil, "", errors.NewNotFound(schema.GroupResource{Group: "apps.openshift.io", Resource: "deploymentconfigs"}, "router") } +// FindImageFormatString returns a format string for components on the cluster. It returns false +// if no format string could be inferred from the cluster. OpenShift 4.0 clusters will not be able +// to infer an image format string, so you must wrap this method in one that can locate your specific +// image. func FindImageFormatString(oc *CLI) (string, bool) { - // the router is expected to be on all clusters - // TODO: switch this to read from the global config + // legacy support for 3.x clusters template, _, err := GetRouterPodTemplate(oc) if err == nil { - return strings.Replace(template.Spec.Containers[0].Image, "haproxy-router", "${component}", -1), true + if strings.Contains(template.Spec.Containers[0].Image, "haproxy-router") { + return strings.Replace(template.Spec.Containers[0].Image, "haproxy-router", "${component}", -1), true + } } + // in openshift 4.0, no image format can be calculated on cluster return "openshift/origin-${component}:latest", false } +func FindCLIImage(oc *CLI) (string, bool) { + // look up image stream + is, err := oc.AdminImageClient().ImageV1().ImageStreams("openshift").Get("cli", metav1.GetOptions{}) + if err == nil { + for _, tag := range is.Spec.Tags { + if tag.Name == "latest" && tag.From != nil && tag.From.Kind == "DockerImage" { + return tag.From.Name, true + } + } + } + + format, ok := FindImageFormatString(oc) + return strings.Replace(format, "${component}", "cli", -1), ok +} + +func FindRouterImage(oc *CLI) (string, bool) { + format, ok := FindImageFormatString(oc) + return strings.Replace(format, "${component}", "haproxy-router", -1), ok +} + func IsClusterOperated(oc *CLI) bool { configclient := oc.AdminConfigClient().ConfigV1() o, err := configclient.Images().Get("cluster", metav1.GetOptions{}) diff --git a/vendor/k8s.io/kubernetes/test/e2e/framework/util.go b/vendor/k8s.io/kubernetes/test/e2e/framework/util.go index a351df4eaf01..c6fb6e118f97 100644 --- a/vendor/k8s.io/kubernetes/test/e2e/framework/util.go +++ b/vendor/k8s.io/kubernetes/test/e2e/framework/util.go @@ -3319,14 +3319,87 @@ func SSH(cmd, host, provider string) (SSHResult, error) { result.User = os.Getenv("USER") } + if bastion := os.Getenv("KUBE_SSH_BASTION"); len(bastion) > 0 { + stdout, stderr, code, err := RunForwardedSSHCommand(cmd, result.User, bastion, host, signer) + result.Stdout = stdout + result.Stderr = stderr + result.Code = code + return result, err + } + stdout, stderr, code, err := sshutil.RunSSHCommand(cmd, result.User, host, signer) result.Stdout = stdout result.Stderr = stderr result.Code = code - return result, err } +// RunForwardedSSHCommand returns the stdout, stderr, and exit code from running cmd on +// host as specific user, along with any SSH-level error. +func RunForwardedSSHCommand(cmd, user, bastion, host string, signer ssh.Signer) (string, string, int, error) { + // Setup the config, dial the server, and open a session. + config := &ssh.ClientConfig{ + User: user, + Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)}, + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + Timeout: 150 * time.Second, + } + bastionClient, err := ssh.Dial("tcp", bastion, config) + if err != nil { + err = wait.Poll(5*time.Second, 20*time.Second, func() (bool, error) { + fmt.Printf("error dialing %s@%s: '%v', retrying\n", user, bastion, err) + if bastionClient, err = ssh.Dial("tcp", bastion, config); err != nil { + return false, err + } + return true, nil + }) + } + if err != nil { + return "", "", 0, fmt.Errorf("error getting SSH client to %s@%s: %v", user, bastion, err) + } + defer bastionClient.Close() + + conn, err := bastionClient.Dial("tcp", host) + if err != nil { + return "", "", 0, fmt.Errorf("error dialing %s from bastion: %v", host, err) + } + defer conn.Close() + + ncc, chans, reqs, err := ssh.NewClientConn(conn, host, config) + if err != nil { + return "", "", 0, fmt.Errorf("error creating forwarding connection %s from bastion: %v", host, err) + } + client := ssh.NewClient(ncc, chans, reqs) + defer client.Close() + + session, err := client.NewSession() + if err != nil { + return "", "", 0, fmt.Errorf("error creating session to %s@%s from bastion: '%v'", user, host, err) + } + defer session.Close() + + // Run the command. + code := 0 + var bout, berr bytes.Buffer + session.Stdout, session.Stderr = &bout, &berr + if err = session.Run(cmd); err != nil { + // Check whether the command failed to run or didn't complete. + if exiterr, ok := err.(*ssh.ExitError); ok { + // If we got an ExitError and the exit code is nonzero, we'll + // consider the SSH itself successful (just that the command run + // errored on the host). + if code = exiterr.ExitStatus(); code != 0 { + err = nil + } + } else { + // Some other kind of error happened (e.g. an IOError); consider the + // SSH unsuccessful. + err = fmt.Errorf("failed running `%s` on %s@%s: '%v'", cmd, user, host, err) + } + } + return bout.String(), berr.String(), code, err +} + func LogSSHResult(result SSHResult) { remote := fmt.Sprintf("%s@%s", result.User, result.Host) Logf("ssh %s: command: %s", remote, result.Cmd) @@ -3529,38 +3602,40 @@ func GetSigner(provider string) (ssh.Signer, error) { // Get the directory in which SSH keys are located. keydir := filepath.Join(os.Getenv("HOME"), ".ssh") - // Select the key itself to use. When implementing more providers here, - // please also add them to any SSH tests that are disabled because of signer - // support. - keyfile := "" - key := "" - switch provider { - case "gce", "gke", "kubemark": - keyfile = "google_compute_engine" - case "aws": - // If there is an env. variable override, use that. - aws_keyfile := os.Getenv("AWS_SSH_KEY") - if len(aws_keyfile) != 0 { - return sshutil.MakePrivateKeySignerFromFile(aws_keyfile) - } - // Otherwise revert to home dir - keyfile = "kube_aws_rsa" - case "local", "vsphere": - keyfile = os.Getenv("LOCAL_SSH_KEY") // maybe? - if len(keyfile) == 0 { - keyfile = "id_rsa" - } - case "skeleton": - keyfile = os.Getenv("KUBE_SSH_KEY") - if len(keyfile) == 0 { - keyfile = "id_rsa" - } - default: - return nil, fmt.Errorf("GetSigner(...) not implemented for %s", provider) - } + key := os.Getenv("KUBE_SSH_KEY_FILE") if len(key) == 0 { - key = filepath.Join(keydir, keyfile) + // Select the key itself to use. When implementing more providers here, + // please also add them to any SSH tests that are disabled because of signer + // support. + keyfile := "" + switch provider { + case "gce", "gke", "kubemark": + keyfile = "google_compute_engine" + case "aws": + // If there is an env. variable override, use that. + aws_keyfile := os.Getenv("AWS_SSH_KEY") + if len(aws_keyfile) != 0 { + return sshutil.MakePrivateKeySignerFromFile(aws_keyfile) + } + // Otherwise revert to home dir + keyfile = "kube_aws_rsa" + case "local", "vsphere": + keyfile = os.Getenv("LOCAL_SSH_KEY") // maybe? + if len(keyfile) == 0 { + keyfile = "id_rsa" + } + case "skeleton": + keyfile = os.Getenv("KUBE_SSH_KEY") + if len(keyfile) == 0 { + keyfile = "id_rsa" + } + default: + return nil, fmt.Errorf("GetSigner(...) not implemented for %s", provider) + } + if len(key) == 0 { + key = filepath.Join(keydir, keyfile) + } } return sshutil.MakePrivateKeySignerFromFile(key)