2015-02-20 17:35:42 +00:00
|
|
|
/*
|
2015-05-01 16:19:44 +00:00
|
|
|
Copyright 2015 The Kubernetes Authors All rights reserved.
|
2015-02-20 17:35:42 +00:00
|
|
|
|
|
|
|
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 permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package e2e
|
|
|
|
|
|
|
|
import (
|
2015-09-10 21:32:57 +00:00
|
|
|
"bytes"
|
2015-02-20 17:35:42 +00:00
|
|
|
"encoding/json"
|
2015-03-26 20:34:18 +00:00
|
|
|
"errors"
|
2015-03-26 19:22:04 +00:00
|
|
|
"fmt"
|
2015-08-31 17:23:47 +00:00
|
|
|
"io"
|
2015-06-30 16:43:37 +00:00
|
|
|
"io/ioutil"
|
2015-09-17 20:11:27 +00:00
|
|
|
"mime/multipart"
|
2015-07-07 10:39:38 +00:00
|
|
|
"net"
|
2015-06-30 16:43:37 +00:00
|
|
|
"net/http"
|
2015-10-19 18:17:02 +00:00
|
|
|
"net/url"
|
2015-07-07 10:39:38 +00:00
|
|
|
"os"
|
2015-07-07 05:09:22 +00:00
|
|
|
"os/exec"
|
2015-09-29 15:20:49 +00:00
|
|
|
"path"
|
2015-02-20 17:35:42 +00:00
|
|
|
"path/filepath"
|
2015-06-30 16:43:37 +00:00
|
|
|
"regexp"
|
2015-07-07 05:09:22 +00:00
|
|
|
"strconv"
|
2015-02-20 17:35:42 +00:00
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
2015-09-10 21:32:57 +00:00
|
|
|
"github.com/ghodss/yaml"
|
|
|
|
|
2015-08-05 22:03:47 +00:00
|
|
|
"k8s.io/kubernetes/pkg/api"
|
2015-09-10 01:28:53 +00:00
|
|
|
apierrs "k8s.io/kubernetes/pkg/api/errors"
|
2015-10-09 01:32:15 +00:00
|
|
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
2015-08-13 19:01:50 +00:00
|
|
|
client "k8s.io/kubernetes/pkg/client/unversioned"
|
2015-08-05 22:03:47 +00:00
|
|
|
"k8s.io/kubernetes/pkg/fields"
|
2015-09-10 21:32:57 +00:00
|
|
|
"k8s.io/kubernetes/pkg/kubectl"
|
2015-11-13 01:07:21 +00:00
|
|
|
"k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
2015-08-05 22:03:47 +00:00
|
|
|
"k8s.io/kubernetes/pkg/labels"
|
2015-09-10 01:28:53 +00:00
|
|
|
"k8s.io/kubernetes/pkg/util/wait"
|
2015-03-05 04:35:51 +00:00
|
|
|
|
2015-02-20 17:35:42 +00:00
|
|
|
. "github.com/onsi/ginkgo"
|
2015-04-30 02:53:09 +00:00
|
|
|
. "github.com/onsi/gomega"
|
2015-02-20 17:35:42 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2015-03-31 17:25:20 +00:00
|
|
|
nautilusImage = "gcr.io/google_containers/update-demo:nautilus"
|
|
|
|
kittenImage = "gcr.io/google_containers/update-demo:kitten"
|
2015-03-13 16:26:41 +00:00
|
|
|
updateDemoSelector = "name=update-demo"
|
|
|
|
updateDemoContainer = "update-demo"
|
2015-09-23 09:37:47 +00:00
|
|
|
frontendSelector = "app=guestbook,tier=frontend"
|
|
|
|
redisMasterSelector = "app=redis,role=master"
|
|
|
|
redisSlaveSelector = "app=redis,role=slave"
|
2015-09-17 20:11:27 +00:00
|
|
|
goproxyContainer = "goproxy"
|
|
|
|
goproxyPodSelector = "name=goproxy"
|
|
|
|
netexecContainer = "netexec"
|
|
|
|
netexecPodSelector = "name=netexec"
|
2015-03-13 16:26:41 +00:00
|
|
|
kubectlProxyPort = 8011
|
2015-03-18 14:16:26 +00:00
|
|
|
guestbookStartupTimeout = 10 * time.Minute
|
2015-03-24 12:59:45 +00:00
|
|
|
guestbookResponseTimeout = 3 * time.Minute
|
2015-06-30 16:43:37 +00:00
|
|
|
simplePodSelector = "name=nginx"
|
|
|
|
simplePodName = "nginx"
|
|
|
|
nginxDefaultOutput = "Welcome to nginx!"
|
|
|
|
simplePodPort = 80
|
|
|
|
)
|
|
|
|
|
2015-09-22 20:29:51 +00:00
|
|
|
var proxyRegexp = regexp.MustCompile("Starting to serve on 127.0.0.1:([0-9]+)")
|
2015-02-20 17:35:42 +00:00
|
|
|
|
2015-06-03 03:23:44 +00:00
|
|
|
var _ = Describe("Kubectl client", func() {
|
2015-05-19 16:13:08 +00:00
|
|
|
defer GinkgoRecover()
|
2015-10-27 08:24:32 +00:00
|
|
|
framework := NewFramework("kubectl")
|
2015-03-05 04:35:51 +00:00
|
|
|
var c *client.Client
|
2015-04-30 02:53:09 +00:00
|
|
|
var ns string
|
2015-03-02 18:15:34 +00:00
|
|
|
BeforeEach(func() {
|
2015-10-27 08:24:32 +00:00
|
|
|
c = framework.Client
|
|
|
|
ns = framework.Namespace.Name
|
2015-03-02 18:15:34 +00:00
|
|
|
})
|
2015-02-20 17:35:42 +00:00
|
|
|
|
2015-06-03 03:23:44 +00:00
|
|
|
Describe("Update Demo", func() {
|
2015-05-19 16:13:08 +00:00
|
|
|
var updateDemoRoot, nautilusPath, kittenPath string
|
|
|
|
BeforeEach(func() {
|
2015-07-14 22:57:41 +00:00
|
|
|
updateDemoRoot = filepath.Join(testContext.RepoRoot, "docs/user-guide/update-demo")
|
2015-05-19 16:13:08 +00:00
|
|
|
nautilusPath = filepath.Join(updateDemoRoot, "nautilus-rc.yaml")
|
|
|
|
kittenPath = filepath.Join(updateDemoRoot, "kitten-rc.yaml")
|
|
|
|
})
|
2015-10-07 14:09:24 +00:00
|
|
|
It("should create and stop a replication controller [Conformance]", func() {
|
2015-05-15 09:39:30 +00:00
|
|
|
defer cleanup(nautilusPath, ns, updateDemoSelector)
|
2015-03-13 16:26:41 +00:00
|
|
|
|
|
|
|
By("creating a replication controller")
|
2015-11-08 18:58:21 +00:00
|
|
|
runKubectlOrDie("create", "-f", nautilusPath, fmt.Sprintf("--namespace=%v", ns))
|
2015-04-30 02:53:09 +00:00
|
|
|
validateController(c, nautilusImage, 2, "update-demo", updateDemoSelector, getUDData("nautilus.jpg", ns), ns)
|
2015-03-13 16:26:41 +00:00
|
|
|
})
|
|
|
|
|
2015-10-07 14:09:24 +00:00
|
|
|
It("should scale a replication controller [Conformance]", func() {
|
2015-05-15 09:39:30 +00:00
|
|
|
defer cleanup(nautilusPath, ns, updateDemoSelector)
|
2015-03-13 16:26:41 +00:00
|
|
|
|
|
|
|
By("creating a replication controller")
|
2015-11-08 18:58:21 +00:00
|
|
|
runKubectlOrDie("create", "-f", nautilusPath, fmt.Sprintf("--namespace=%v", ns))
|
2015-04-30 02:53:09 +00:00
|
|
|
validateController(c, nautilusImage, 2, "update-demo", updateDemoSelector, getUDData("nautilus.jpg", ns), ns)
|
2015-03-13 16:26:41 +00:00
|
|
|
By("scaling down the replication controller")
|
2015-11-08 18:58:21 +00:00
|
|
|
runKubectlOrDie("scale", "rc", "update-demo-nautilus", "--replicas=1", "--timeout=5m", fmt.Sprintf("--namespace=%v", ns))
|
2015-04-30 02:53:09 +00:00
|
|
|
validateController(c, nautilusImage, 1, "update-demo", updateDemoSelector, getUDData("nautilus.jpg", ns), ns)
|
2015-03-13 16:26:41 +00:00
|
|
|
By("scaling up the replication controller")
|
2015-11-08 18:58:21 +00:00
|
|
|
runKubectlOrDie("scale", "rc", "update-demo-nautilus", "--replicas=2", "--timeout=5m", fmt.Sprintf("--namespace=%v", ns))
|
2015-04-30 02:53:09 +00:00
|
|
|
validateController(c, nautilusImage, 2, "update-demo", updateDemoSelector, getUDData("nautilus.jpg", ns), ns)
|
2015-03-13 16:26:41 +00:00
|
|
|
})
|
|
|
|
|
2015-10-07 14:09:24 +00:00
|
|
|
It("should do a rolling update of a replication controller [Conformance]", func() {
|
2015-03-13 16:26:41 +00:00
|
|
|
By("creating the initial replication controller")
|
2015-11-08 18:58:21 +00:00
|
|
|
runKubectlOrDie("create", "-f", nautilusPath, fmt.Sprintf("--namespace=%v", ns))
|
2015-04-30 02:53:09 +00:00
|
|
|
validateController(c, nautilusImage, 2, "update-demo", updateDemoSelector, getUDData("nautilus.jpg", ns), ns)
|
2015-03-27 23:24:59 +00:00
|
|
|
By("rolling-update to new replication controller")
|
2015-11-08 18:58:21 +00:00
|
|
|
runKubectlOrDie("rolling-update", "update-demo-nautilus", "--update-period=1s", "-f", kittenPath, fmt.Sprintf("--namespace=%v", ns))
|
2015-06-23 03:58:48 +00:00
|
|
|
validateController(c, kittenImage, 2, "update-demo", updateDemoSelector, getUDData("kitten.jpg", ns), ns)
|
2015-05-15 03:10:32 +00:00
|
|
|
// Everything will hopefully be cleaned up when the namespace is deleted.
|
2015-03-13 16:26:41 +00:00
|
|
|
})
|
2015-02-20 17:35:42 +00:00
|
|
|
})
|
|
|
|
|
2015-06-03 03:23:44 +00:00
|
|
|
Describe("Guestbook application", func() {
|
2015-05-19 16:13:08 +00:00
|
|
|
var guestbookPath string
|
2015-06-22 21:14:54 +00:00
|
|
|
|
2015-05-19 16:13:08 +00:00
|
|
|
BeforeEach(func() {
|
|
|
|
guestbookPath = filepath.Join(testContext.RepoRoot, "examples/guestbook")
|
|
|
|
})
|
2015-03-13 16:26:41 +00:00
|
|
|
|
2015-10-07 14:09:24 +00:00
|
|
|
It("should create and stop a working application [Conformance]", func() {
|
2015-05-15 09:39:30 +00:00
|
|
|
defer cleanup(guestbookPath, ns, frontendSelector, redisMasterSelector, redisSlaveSelector)
|
2015-02-20 17:35:42 +00:00
|
|
|
|
2015-03-13 16:26:41 +00:00
|
|
|
By("creating all guestbook components")
|
2015-11-08 18:58:21 +00:00
|
|
|
runKubectlOrDie("create", "-f", guestbookPath, fmt.Sprintf("--namespace=%v", ns))
|
2015-02-20 17:35:42 +00:00
|
|
|
|
2015-03-13 16:26:41 +00:00
|
|
|
By("validating guestbook app")
|
2015-04-30 02:53:09 +00:00
|
|
|
validateGuestbookApp(c, ns)
|
2015-03-13 16:26:41 +00:00
|
|
|
})
|
2015-02-20 17:35:42 +00:00
|
|
|
})
|
|
|
|
|
2015-06-30 16:43:37 +00:00
|
|
|
Describe("Simple pod", func() {
|
|
|
|
var podPath string
|
|
|
|
|
|
|
|
BeforeEach(func() {
|
2015-07-14 22:57:41 +00:00
|
|
|
podPath = filepath.Join(testContext.RepoRoot, "docs/user-guide/pod.yaml")
|
2015-06-30 16:43:37 +00:00
|
|
|
By("creating the pod")
|
2015-11-08 18:58:21 +00:00
|
|
|
runKubectlOrDie("create", "-f", podPath, fmt.Sprintf("--namespace=%v", ns))
|
2015-06-30 16:43:37 +00:00
|
|
|
checkPodsRunningReady(c, ns, []string{simplePodName}, podStartTimeout)
|
|
|
|
})
|
|
|
|
AfterEach(func() {
|
|
|
|
cleanup(podPath, ns, simplePodSelector)
|
|
|
|
})
|
|
|
|
|
|
|
|
It("should support exec", func() {
|
|
|
|
By("executing a command in the container")
|
2015-11-08 18:58:21 +00:00
|
|
|
execOutput := runKubectlOrDie("exec", fmt.Sprintf("--namespace=%v", ns), simplePodName, "echo", "running", "in", "container")
|
2015-08-31 17:23:47 +00:00
|
|
|
if e, a := "running in container", execOutput; e != a {
|
|
|
|
Failf("Unexpected kubectl exec output. Wanted %q, got %q", e, a)
|
|
|
|
}
|
|
|
|
|
|
|
|
By("executing a command in the container with noninteractive stdin")
|
|
|
|
execOutput = newKubectlCommand("exec", fmt.Sprintf("--namespace=%v", ns), "-i", simplePodName, "cat").
|
|
|
|
withStdinData("abcd1234").
|
2015-11-08 18:58:21 +00:00
|
|
|
execOrDie()
|
2015-08-31 17:23:47 +00:00
|
|
|
if e, a := "abcd1234", execOutput; e != a {
|
|
|
|
Failf("Unexpected kubectl exec output. Wanted %q, got %q", e, a)
|
|
|
|
}
|
|
|
|
|
|
|
|
// pretend that we're a user in an interactive shell
|
2015-09-17 20:11:27 +00:00
|
|
|
r, closer, err := newBlockingReader("echo hi\nexit\n")
|
2015-08-31 17:23:47 +00:00
|
|
|
if err != nil {
|
|
|
|
Failf("Error creating blocking reader: %v", err)
|
|
|
|
}
|
|
|
|
// NOTE this is solely for test cleanup!
|
2015-09-17 20:11:27 +00:00
|
|
|
defer closer.Close()
|
2015-08-31 17:23:47 +00:00
|
|
|
|
|
|
|
By("executing a command in the container with pseudo-interactive stdin")
|
|
|
|
execOutput = newKubectlCommand("exec", fmt.Sprintf("--namespace=%v", ns), "-i", simplePodName, "bash").
|
|
|
|
withStdinReader(r).
|
2015-11-08 18:58:21 +00:00
|
|
|
execOrDie()
|
2015-08-31 17:23:47 +00:00
|
|
|
if e, a := "hi", execOutput; e != a {
|
|
|
|
Failf("Unexpected kubectl exec output. Wanted %q, got %q", e, a)
|
2015-06-30 16:43:37 +00:00
|
|
|
}
|
|
|
|
})
|
2015-08-31 17:23:47 +00:00
|
|
|
|
2015-10-19 18:17:02 +00:00
|
|
|
It("should support exec through an HTTP proxy", func() {
|
2015-09-17 20:11:27 +00:00
|
|
|
// Note: We are skipping local since we want to verify an apiserver with HTTPS.
|
|
|
|
// At this time local only supports plain HTTP.
|
|
|
|
SkipIfProviderIs("local")
|
|
|
|
// Fail if the variable isn't set
|
|
|
|
if testContext.Host == "" {
|
|
|
|
Failf("--host variable must be set to the full URI to the api server on e2e run.")
|
|
|
|
}
|
2015-10-19 18:17:02 +00:00
|
|
|
|
|
|
|
// Make sure the apiServer is set to what kubectl requires
|
2015-09-17 20:11:27 +00:00
|
|
|
apiServer := testContext.Host
|
2015-10-19 18:17:02 +00:00
|
|
|
apiServerUrl, err := url.Parse(apiServer)
|
|
|
|
if err != nil {
|
|
|
|
Failf("Unable to parse URL %s. Error=%s", apiServer, err)
|
2015-09-17 20:11:27 +00:00
|
|
|
}
|
2015-10-19 18:17:02 +00:00
|
|
|
apiServerUrl.Scheme = "https"
|
|
|
|
apiServerUrl.Path = "/api"
|
|
|
|
if !strings.Contains(apiServer, ":443") {
|
|
|
|
apiServerUrl.Host = apiServerUrl.Host + ":443"
|
2015-09-17 20:11:27 +00:00
|
|
|
}
|
2015-10-19 18:17:02 +00:00
|
|
|
apiServer = apiServerUrl.String()
|
2015-09-17 20:11:27 +00:00
|
|
|
|
2015-09-29 15:20:49 +00:00
|
|
|
// Build the static kubectl
|
2015-10-19 18:17:02 +00:00
|
|
|
By("Finding a static kubectl for upload")
|
2015-11-02 17:58:50 +00:00
|
|
|
testStaticKubectlPath, err := findBinary("kubectl", "linux/386")
|
2015-09-29 15:20:49 +00:00
|
|
|
if err != nil {
|
2015-11-02 17:58:50 +00:00
|
|
|
Logf("No kubectl found: %v.\nAttempting a local build...", err)
|
2015-10-19 18:17:02 +00:00
|
|
|
// Fall back to trying to build a local static kubectl
|
|
|
|
kubectlContainerPath := path.Join(testContext.RepoRoot, "/examples/kubectl-container/")
|
|
|
|
if _, err := os.Stat(path.Join(testContext.RepoRoot, "hack/build-go.sh")); err != nil {
|
|
|
|
Failf("Can't build static kubectl due to missing hack/build-go.sh. Error=%s", err)
|
|
|
|
}
|
|
|
|
By("Building a static kubectl for upload")
|
|
|
|
staticKubectlBuild := exec.Command("make", "-C", kubectlContainerPath)
|
|
|
|
if out, err := staticKubectlBuild.Output(); err != nil {
|
|
|
|
Failf("Unable to create static kubectl. Error=%s, Output=%q", err, out)
|
|
|
|
}
|
|
|
|
// Verify the static kubectl path
|
|
|
|
testStaticKubectlPath = path.Join(kubectlContainerPath, "kubectl")
|
|
|
|
_, err := os.Stat(testStaticKubectlPath)
|
|
|
|
if err != nil {
|
|
|
|
Failf("static kubectl path could not be found in %s. Error=%s", testStaticKubectlPath, err)
|
|
|
|
}
|
2015-09-17 20:11:27 +00:00
|
|
|
}
|
2015-10-19 18:17:02 +00:00
|
|
|
By(fmt.Sprintf("Using the kubectl in %s", testStaticKubectlPath))
|
2015-09-17 20:11:27 +00:00
|
|
|
|
2015-09-29 15:20:49 +00:00
|
|
|
// Verify the kubeconfig path
|
|
|
|
kubeConfigFilePath := testContext.KubeConfig
|
|
|
|
_, err = os.Stat(kubeConfigFilePath)
|
2015-09-17 20:11:27 +00:00
|
|
|
if err != nil {
|
|
|
|
Failf("kube config path could not be accessed. Error=%s", err)
|
|
|
|
}
|
|
|
|
// start exec-proxy-tester container
|
|
|
|
netexecPodPath := filepath.Join(testContext.RepoRoot, "test/images/netexec/pod.yaml")
|
2015-11-08 18:58:21 +00:00
|
|
|
runKubectlOrDie("create", "-f", netexecPodPath, fmt.Sprintf("--namespace=%v", ns))
|
2015-09-17 20:11:27 +00:00
|
|
|
checkPodsRunningReady(c, ns, []string{netexecContainer}, podStartTimeout)
|
|
|
|
// Clean up
|
|
|
|
defer cleanup(netexecPodPath, ns, netexecPodSelector)
|
|
|
|
// Upload kubeconfig
|
|
|
|
type NetexecOutput struct {
|
|
|
|
Output string `json:"output"`
|
|
|
|
Error string `json:"error"`
|
|
|
|
}
|
|
|
|
|
|
|
|
var uploadConfigOutput NetexecOutput
|
|
|
|
// Upload the kubeconfig file
|
|
|
|
By("uploading kubeconfig to netexec")
|
|
|
|
pipeConfigReader, postConfigBodyWriter, err := newStreamingUpload(kubeConfigFilePath)
|
|
|
|
if err != nil {
|
|
|
|
Failf("unable to create streaming upload. Error: %s", err)
|
|
|
|
}
|
|
|
|
resp, err := c.Post().
|
|
|
|
Namespace(ns).
|
|
|
|
Name("netexec").
|
|
|
|
Resource("pods").
|
2015-11-09 07:23:48 +00:00
|
|
|
SubResource("proxy").
|
2015-09-17 20:11:27 +00:00
|
|
|
Suffix("upload").
|
|
|
|
SetHeader("Content-Type", postConfigBodyWriter.FormDataContentType()).
|
|
|
|
Body(pipeConfigReader).
|
|
|
|
Do().Raw()
|
|
|
|
if err != nil {
|
|
|
|
Failf("Unable to upload kubeconfig to the remote exec server due to error: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := json.Unmarshal(resp, &uploadConfigOutput); err != nil {
|
|
|
|
Failf("Unable to read the result from the netexec server. Error: %s", err)
|
|
|
|
}
|
|
|
|
kubecConfigRemotePath := uploadConfigOutput.Output
|
|
|
|
|
|
|
|
// Upload
|
2015-09-29 15:20:49 +00:00
|
|
|
pipeReader, postBodyWriter, err := newStreamingUpload(testStaticKubectlPath)
|
2015-09-17 20:11:27 +00:00
|
|
|
if err != nil {
|
|
|
|
Failf("unable to create streaming upload. Error: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
By("uploading kubectl to netexec")
|
|
|
|
var uploadOutput NetexecOutput
|
|
|
|
// Upload the kubectl binary
|
|
|
|
resp, err = c.Post().
|
|
|
|
Namespace(ns).
|
|
|
|
Name("netexec").
|
|
|
|
Resource("pods").
|
2015-11-09 07:23:48 +00:00
|
|
|
SubResource("proxy").
|
2015-09-17 20:11:27 +00:00
|
|
|
Suffix("upload").
|
|
|
|
SetHeader("Content-Type", postBodyWriter.FormDataContentType()).
|
|
|
|
Body(pipeReader).
|
|
|
|
Do().Raw()
|
|
|
|
if err != nil {
|
|
|
|
Failf("Unable to upload kubectl binary to the remote exec server due to error: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := json.Unmarshal(resp, &uploadOutput); err != nil {
|
|
|
|
Failf("Unable to read the result from the netexec server. Error: %s", err)
|
|
|
|
}
|
|
|
|
uploadBinaryName := uploadOutput.Output
|
|
|
|
// Verify that we got the expected response back in the body
|
|
|
|
if !strings.HasPrefix(uploadBinaryName, "/uploads/") {
|
|
|
|
Failf("Unable to upload kubectl binary to remote exec server. /uploads/ not in response. Response: %s", uploadBinaryName)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, proxyVar := range []string{"https_proxy", "HTTPS_PROXY"} {
|
|
|
|
By("Running kubectl in netexec via an HTTP proxy using " + proxyVar)
|
|
|
|
// start the proxy container
|
|
|
|
goproxyPodPath := filepath.Join(testContext.RepoRoot, "test/images/goproxy/pod.yaml")
|
2015-11-08 18:58:21 +00:00
|
|
|
runKubectlOrDie("create", "-f", goproxyPodPath, fmt.Sprintf("--namespace=%v", ns))
|
2015-09-17 20:11:27 +00:00
|
|
|
checkPodsRunningReady(c, ns, []string{goproxyContainer}, podStartTimeout)
|
|
|
|
|
|
|
|
// get the proxy address
|
|
|
|
goproxyPod, err := c.Pods(ns).Get(goproxyContainer)
|
|
|
|
if err != nil {
|
|
|
|
Failf("Unable to get the goproxy pod. Error: %s", err)
|
|
|
|
}
|
|
|
|
proxyAddr := fmt.Sprintf("http://%s:8080", goproxyPod.Status.PodIP)
|
|
|
|
|
|
|
|
shellCommand := fmt.Sprintf("%s=%s .%s --kubeconfig=%s --server=%s --namespace=%s exec nginx echo running in container", proxyVar, proxyAddr, uploadBinaryName, kubecConfigRemotePath, apiServer, ns)
|
|
|
|
// Execute kubectl on remote exec server.
|
|
|
|
netexecShellOutput, err := c.Post().
|
|
|
|
Namespace(ns).
|
|
|
|
Name("netexec").
|
|
|
|
Resource("pods").
|
2015-11-09 07:23:48 +00:00
|
|
|
SubResource("proxy").
|
2015-09-17 20:11:27 +00:00
|
|
|
Suffix("shell").
|
|
|
|
Param("shellCommand", shellCommand).
|
|
|
|
Do().Raw()
|
|
|
|
if err != nil {
|
|
|
|
Failf("Unable to execute kubectl binary on the remote exec server due to error: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var netexecOuput NetexecOutput
|
|
|
|
if err := json.Unmarshal(netexecShellOutput, &netexecOuput); err != nil {
|
|
|
|
Failf("Unable to read the result from the netexec server. Error: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify we got the normal output captured by the exec server
|
|
|
|
expectedExecOutput := "running in container\n"
|
|
|
|
if netexecOuput.Output != expectedExecOutput {
|
|
|
|
Failf("Unexpected kubectl exec output. Wanted %q, got %q", expectedExecOutput, netexecOuput.Output)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the proxy server logs saw the connection
|
|
|
|
expectedProxyLog := fmt.Sprintf("Accepting CONNECT to %s", strings.TrimRight(strings.TrimLeft(testContext.Host, "https://"), "/api"))
|
2015-11-08 18:58:21 +00:00
|
|
|
proxyLog := runKubectlOrDie("log", "goproxy", fmt.Sprintf("--namespace=%v", ns))
|
2015-09-17 20:11:27 +00:00
|
|
|
|
|
|
|
if !strings.Contains(proxyLog, expectedProxyLog) {
|
|
|
|
Failf("Missing expected log result on proxy server for %s. Expected: %q, got %q", proxyVar, expectedProxyLog, proxyLog)
|
|
|
|
}
|
|
|
|
// Clean up the goproxyPod
|
|
|
|
cleanup(goproxyPodPath, ns, goproxyPodSelector)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2015-09-01 23:42:16 +00:00
|
|
|
It("should support inline execution and attach", func() {
|
2015-10-06 15:31:48 +00:00
|
|
|
nsFlag := fmt.Sprintf("--namespace=%v", ns)
|
|
|
|
|
|
|
|
By("executing a command with run and attach with stdin")
|
|
|
|
runOutput := newKubectlCommand(nsFlag, "run", "run-test", "--image=busybox", "--restart=Never", "--attach=true", "--stdin", "--", "sh", "-c", "cat && echo 'stdin closed'").
|
|
|
|
withStdinData("abcd1234").
|
2015-11-08 18:58:21 +00:00
|
|
|
execOrDie()
|
2015-10-06 15:31:48 +00:00
|
|
|
Expect(runOutput).To(ContainSubstring("abcd1234"))
|
|
|
|
Expect(runOutput).To(ContainSubstring("stdin closed"))
|
2015-11-13 01:07:21 +00:00
|
|
|
Expect(c.Extensions().Jobs(ns).Delete("run-test", api.NewDeleteOptions(0))).To(BeNil())
|
2015-10-06 15:31:48 +00:00
|
|
|
|
|
|
|
By("executing a command with run and attach without stdin")
|
|
|
|
runOutput = newKubectlCommand(fmt.Sprintf("--namespace=%v", ns), "run", "run-test-2", "--image=busybox", "--restart=Never", "--attach=true", "--leave-stdin-open=true", "--", "sh", "-c", "cat && echo 'stdin closed'").
|
|
|
|
withStdinData("abcd1234").
|
2015-11-08 18:58:21 +00:00
|
|
|
execOrDie()
|
2015-10-06 15:31:48 +00:00
|
|
|
Expect(runOutput).ToNot(ContainSubstring("abcd1234"))
|
|
|
|
Expect(runOutput).To(ContainSubstring("stdin closed"))
|
2015-11-13 01:07:21 +00:00
|
|
|
Expect(c.Extensions().Jobs(ns).Delete("run-test-2", api.NewDeleteOptions(0))).To(BeNil())
|
2015-10-06 15:31:48 +00:00
|
|
|
|
|
|
|
By("executing a command with run and attach with stdin with open stdin should remain running")
|
|
|
|
runOutput = newKubectlCommand(nsFlag, "run", "run-test-3", "--image=busybox", "--restart=Never", "--attach=true", "--leave-stdin-open=true", "--stdin", "--", "sh", "-c", "cat && echo 'stdin closed'").
|
|
|
|
withStdinData("abcd1234\n").
|
2015-11-08 18:58:21 +00:00
|
|
|
execOrDie()
|
2015-10-06 15:31:48 +00:00
|
|
|
Expect(runOutput).ToNot(ContainSubstring("stdin closed"))
|
2015-11-13 01:07:21 +00:00
|
|
|
runTestPod, err := util.GetFirstPod(c, ns, map[string]string{"run": "run-test-3"})
|
|
|
|
if err != nil {
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
if !checkPodsRunningReady(c, ns, []string{runTestPod.Name}, time.Minute) {
|
|
|
|
Failf("Pod %q of Job %q should still be running", runTestPod.Name, "run-test-3")
|
2015-10-06 15:31:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE: we cannot guarantee our output showed up in the container logs before stdin was closed, so we have
|
|
|
|
// to loop test.
|
2015-11-13 01:07:21 +00:00
|
|
|
err = wait.PollImmediate(time.Second, time.Minute, func() (bool, error) {
|
|
|
|
if !checkPodsRunningReady(c, ns, []string{runTestPod.Name}, 1*time.Second) {
|
|
|
|
Failf("Pod %q of Job %q should still be running", runTestPod.Name, "run-test-3")
|
2015-10-06 15:31:48 +00:00
|
|
|
}
|
2015-11-13 01:07:21 +00:00
|
|
|
logOutput := runKubectlOrDie(nsFlag, "logs", runTestPod.Name)
|
2015-10-06 15:31:48 +00:00
|
|
|
Expect(logOutput).ToNot(ContainSubstring("stdin closed"))
|
|
|
|
return strings.Contains(logOutput, "abcd1234"), nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
Expect(err).To(BeNil())
|
|
|
|
|
2015-11-13 01:07:21 +00:00
|
|
|
Expect(c.Extensions().Jobs(ns).Delete("run-test-3", api.NewDeleteOptions(0))).To(BeNil())
|
2015-09-01 23:42:16 +00:00
|
|
|
})
|
|
|
|
|
2015-06-30 16:43:37 +00:00
|
|
|
It("should support port-forward", func() {
|
|
|
|
By("forwarding the container port to a local port")
|
2015-09-22 20:29:51 +00:00
|
|
|
cmd, listenPort := runPortForward(ns, simplePodName, simplePodPort)
|
2015-07-07 05:09:22 +00:00
|
|
|
defer tryKill(cmd)
|
2015-09-22 20:29:51 +00:00
|
|
|
|
2015-06-30 16:43:37 +00:00
|
|
|
By("curling local port output")
|
2015-09-22 20:29:51 +00:00
|
|
|
localAddr := fmt.Sprintf("http://localhost:%d", listenPort)
|
2015-06-30 16:43:37 +00:00
|
|
|
body, err := curl(localAddr)
|
2015-07-07 05:09:22 +00:00
|
|
|
Logf("got: %s", body)
|
2015-06-30 16:43:37 +00:00
|
|
|
if err != nil {
|
|
|
|
Failf("Failed http.Get of forwarded port (%s): %v", localAddr, err)
|
|
|
|
}
|
|
|
|
if !strings.Contains(body, nginxDefaultOutput) {
|
|
|
|
Failf("Container port output missing expected value. Wanted:'%s', got: %s", nginxDefaultOutput, body)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2015-07-24 08:20:35 +00:00
|
|
|
Describe("Kubectl api-versions", func() {
|
2015-10-07 14:09:24 +00:00
|
|
|
It("should check if v1 is in available api versions [Conformance]", func() {
|
2015-07-24 08:20:35 +00:00
|
|
|
By("validating api verions")
|
2015-11-08 18:58:21 +00:00
|
|
|
output := runKubectlOrDie("api-versions")
|
2015-07-24 08:20:35 +00:00
|
|
|
if !strings.Contains(output, "v1") {
|
|
|
|
Failf("No v1 in kubectl api-versions")
|
|
|
|
}
|
2015-07-09 00:14:28 +00:00
|
|
|
})
|
2015-07-24 08:20:35 +00:00
|
|
|
})
|
2015-07-09 00:14:28 +00:00
|
|
|
|
2015-09-10 21:32:57 +00:00
|
|
|
Describe("Kubectl apply", func() {
|
|
|
|
It("should apply a new configuration to an existing RC", func() {
|
|
|
|
mkpath := func(file string) string {
|
|
|
|
return filepath.Join(testContext.RepoRoot, "examples/guestbook-go", file)
|
|
|
|
}
|
|
|
|
controllerJson := mkpath("redis-master-controller.json")
|
|
|
|
nsFlag := fmt.Sprintf("--namespace=%v", ns)
|
|
|
|
By("creating Redis RC")
|
2015-11-08 18:58:21 +00:00
|
|
|
runKubectlOrDie("create", "-f", controllerJson, nsFlag)
|
2015-09-10 21:32:57 +00:00
|
|
|
By("applying a modified configuration")
|
|
|
|
stdin := modifyReplicationControllerConfiguration(controllerJson)
|
|
|
|
newKubectlCommand("apply", "-f", "-", nsFlag).
|
|
|
|
withStdinReader(stdin).
|
2015-11-08 18:58:21 +00:00
|
|
|
execOrDie()
|
2015-09-10 21:32:57 +00:00
|
|
|
By("checking the result")
|
|
|
|
forEachReplicationController(c, ns, "app", "redis", validateReplicationControllerConfiguration)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2015-07-24 08:20:35 +00:00
|
|
|
Describe("Kubectl cluster-info", func() {
|
2015-10-07 14:09:24 +00:00
|
|
|
It("should check if Kubernetes master services is included in cluster-info [Conformance]", func() {
|
2015-07-24 08:20:35 +00:00
|
|
|
By("validating cluster-info")
|
2015-11-08 18:58:21 +00:00
|
|
|
output := runKubectlOrDie("cluster-info")
|
2015-08-08 21:29:57 +00:00
|
|
|
// Can't check exact strings due to terminal control commands (colors)
|
2015-07-24 08:20:35 +00:00
|
|
|
requiredItems := []string{"Kubernetes master", "is running at"}
|
|
|
|
if providerIs("gce", "gke") {
|
|
|
|
requiredItems = append(requiredItems, "KubeDNS", "Heapster")
|
|
|
|
}
|
|
|
|
for _, item := range requiredItems {
|
|
|
|
if !strings.Contains(output, item) {
|
|
|
|
Failf("Missing %s in kubectl cluster-info", item)
|
|
|
|
}
|
2015-07-09 00:14:28 +00:00
|
|
|
}
|
2015-07-24 08:20:35 +00:00
|
|
|
})
|
|
|
|
})
|
2015-07-09 00:14:28 +00:00
|
|
|
|
2015-07-24 08:20:35 +00:00
|
|
|
Describe("Kubectl describe", func() {
|
2015-10-07 14:09:24 +00:00
|
|
|
It("should check if kubectl describe prints relevant information for rc and pods [Conformance]", func() {
|
2015-07-24 08:20:35 +00:00
|
|
|
mkpath := func(file string) string {
|
|
|
|
return filepath.Join(testContext.RepoRoot, "examples/guestbook-go", file)
|
2015-07-09 00:14:28 +00:00
|
|
|
}
|
2015-07-24 08:20:35 +00:00
|
|
|
controllerJson := mkpath("redis-master-controller.json")
|
|
|
|
serviceJson := mkpath("redis-master-service.json")
|
|
|
|
|
|
|
|
nsFlag := fmt.Sprintf("--namespace=%v", ns)
|
2015-11-08 18:58:21 +00:00
|
|
|
runKubectlOrDie("create", "-f", controllerJson, nsFlag)
|
|
|
|
runKubectlOrDie("create", "-f", serviceJson, nsFlag)
|
2015-07-24 08:20:35 +00:00
|
|
|
|
|
|
|
// Pod
|
|
|
|
forEachPod(c, ns, "app", "redis", func(pod api.Pod) {
|
2015-11-08 18:58:21 +00:00
|
|
|
output := runKubectlOrDie("describe", "pod", pod.Name, nsFlag)
|
2015-07-24 08:20:35 +00:00
|
|
|
requiredStrings := [][]string{
|
|
|
|
{"Name:", "redis-master-"},
|
|
|
|
{"Namespace:", ns},
|
|
|
|
{"Image(s):", "redis"},
|
|
|
|
{"Node:"},
|
|
|
|
{"Labels:", "app=redis", "role=master"},
|
|
|
|
{"Status:", "Running"},
|
|
|
|
{"Reason:"},
|
|
|
|
{"Message:"},
|
|
|
|
{"IP:"},
|
|
|
|
{"Replication Controllers:", "redis-master"}}
|
|
|
|
checkOutput(output, requiredStrings)
|
|
|
|
})
|
|
|
|
|
|
|
|
// Rc
|
2015-11-08 18:58:21 +00:00
|
|
|
output := runKubectlOrDie("describe", "rc", "redis-master", nsFlag)
|
2015-07-24 08:20:35 +00:00
|
|
|
requiredStrings := [][]string{
|
|
|
|
{"Name:", "redis-master"},
|
|
|
|
{"Namespace:", ns},
|
|
|
|
{"Image(s):", "redis"},
|
|
|
|
{"Selector:", "app=redis,role=master"},
|
|
|
|
{"Labels:", "app=redis,role=master"},
|
|
|
|
{"Replicas:", "1 current", "1 desired"},
|
|
|
|
{"Pods Status:", "1 Running", "0 Waiting", "0 Succeeded", "0 Failed"},
|
|
|
|
{"Events:"}}
|
|
|
|
checkOutput(output, requiredStrings)
|
|
|
|
|
|
|
|
// Service
|
2015-11-08 18:58:21 +00:00
|
|
|
output = runKubectlOrDie("describe", "service", "redis-master", nsFlag)
|
2015-07-24 08:20:35 +00:00
|
|
|
requiredStrings = [][]string{
|
|
|
|
{"Name:", "redis-master"},
|
|
|
|
{"Namespace:", ns},
|
|
|
|
{"Labels:", "app=redis", "role=master"},
|
|
|
|
{"Selector:", "app=redis", "role=master"},
|
|
|
|
{"Type:", "ClusterIP"},
|
|
|
|
{"IP:"},
|
|
|
|
{"Port:", "<unnamed>", "6379/TCP"},
|
|
|
|
{"Endpoints:"},
|
|
|
|
{"Session Affinity:", "None"}}
|
|
|
|
checkOutput(output, requiredStrings)
|
|
|
|
|
|
|
|
// Node
|
2015-11-26 10:06:01 +00:00
|
|
|
nodes, err := c.Nodes().List(labels.Everything(), fields.Everything(), unversioned.ListOptions{})
|
2015-07-24 08:20:35 +00:00
|
|
|
Expect(err).NotTo(HaveOccurred())
|
2015-09-10 08:40:22 +00:00
|
|
|
node := nodes.Items[0]
|
2015-11-08 18:58:21 +00:00
|
|
|
output = runKubectlOrDie("describe", "node", node.Name)
|
2015-07-24 08:20:35 +00:00
|
|
|
requiredStrings = [][]string{
|
|
|
|
{"Name:", node.Name},
|
|
|
|
{"Labels:"},
|
|
|
|
{"CreationTimestamp:"},
|
|
|
|
{"Conditions:"},
|
|
|
|
{"Type", "Status", "LastHeartbeatTime", "LastTransitionTime", "Reason", "Message"},
|
|
|
|
{"Addresses:"},
|
|
|
|
{"Capacity:"},
|
|
|
|
{"Version:"},
|
|
|
|
{"Kernel Version:"},
|
|
|
|
{"OS Image:"},
|
|
|
|
{"Container Runtime Version:"},
|
|
|
|
{"Kubelet Version:"},
|
|
|
|
{"Kube-Proxy Version:"},
|
|
|
|
{"Pods:"}}
|
|
|
|
checkOutput(output, requiredStrings)
|
|
|
|
|
|
|
|
// Namespace
|
2015-11-08 18:58:21 +00:00
|
|
|
output = runKubectlOrDie("describe", "namespace", ns)
|
2015-07-24 08:20:35 +00:00
|
|
|
requiredStrings = [][]string{
|
|
|
|
{"Name:", ns},
|
|
|
|
{"Labels:"},
|
|
|
|
{"Status:", "Active"}}
|
|
|
|
checkOutput(output, requiredStrings)
|
|
|
|
|
|
|
|
// Quota and limitrange are skipped for now.
|
2015-07-09 00:14:28 +00:00
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2015-07-14 09:37:30 +00:00
|
|
|
Describe("Kubectl expose", func() {
|
2015-10-07 14:09:24 +00:00
|
|
|
It("should create services for rc [Conformance]", func() {
|
2015-07-14 09:37:30 +00:00
|
|
|
mkpath := func(file string) string {
|
|
|
|
return filepath.Join(testContext.RepoRoot, "examples/guestbook-go", file)
|
|
|
|
}
|
|
|
|
controllerJson := mkpath("redis-master-controller.json")
|
|
|
|
nsFlag := fmt.Sprintf("--namespace=%v", ns)
|
|
|
|
|
|
|
|
redisPort := 6379
|
|
|
|
|
|
|
|
By("creating Redis RC")
|
2015-11-08 18:58:21 +00:00
|
|
|
runKubectlOrDie("create", "-f", controllerJson, nsFlag)
|
2015-07-14 09:37:30 +00:00
|
|
|
forEachPod(c, ns, "app", "redis", func(pod api.Pod) {
|
|
|
|
lookForStringInLog(ns, pod.Name, "redis-master", "The server is now ready to accept connections", podStartTimeout)
|
|
|
|
})
|
|
|
|
validateService := func(name string, servicePort int, timeout time.Duration) {
|
2015-09-10 01:28:53 +00:00
|
|
|
err := wait.Poll(poll, timeout, func() (bool, error) {
|
2015-07-14 09:37:30 +00:00
|
|
|
endpoints, err := c.Endpoints(ns).Get(name)
|
2015-09-16 23:20:40 +00:00
|
|
|
if err != nil {
|
2015-09-10 01:28:53 +00:00
|
|
|
if apierrs.IsNotFound(err) {
|
|
|
|
err = nil
|
|
|
|
}
|
|
|
|
Logf("Get endpoints failed (interval %v): %v", poll, err)
|
|
|
|
return false, err
|
2015-09-16 23:20:40 +00:00
|
|
|
}
|
2015-07-14 09:37:30 +00:00
|
|
|
|
2015-08-04 13:24:37 +00:00
|
|
|
uidToPort := getContainerPortsByPodUID(endpoints)
|
2015-06-26 08:08:48 +00:00
|
|
|
if len(uidToPort) == 0 {
|
|
|
|
Logf("No endpoint found, retrying")
|
2015-09-10 01:28:53 +00:00
|
|
|
return false, nil
|
2015-07-14 09:37:30 +00:00
|
|
|
}
|
2015-06-26 08:08:48 +00:00
|
|
|
if len(uidToPort) > 1 {
|
2015-09-16 23:20:40 +00:00
|
|
|
Fail("Too many endpoints found")
|
2015-06-26 08:08:48 +00:00
|
|
|
}
|
|
|
|
for _, port := range uidToPort {
|
2015-07-14 09:37:30 +00:00
|
|
|
if port[0] != redisPort {
|
|
|
|
Failf("Wrong endpoint port: %d", port[0])
|
|
|
|
}
|
|
|
|
}
|
2015-09-10 01:28:53 +00:00
|
|
|
return true, nil
|
|
|
|
})
|
|
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
|
2015-07-14 09:37:30 +00:00
|
|
|
service, err := c.Services(ns).Get(name)
|
|
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
|
|
|
|
if len(service.Spec.Ports) != 1 {
|
|
|
|
Failf("1 port is expected")
|
|
|
|
}
|
|
|
|
port := service.Spec.Ports[0]
|
|
|
|
if port.Port != servicePort {
|
|
|
|
Failf("Wrong service port: %d", port.Port)
|
|
|
|
}
|
2015-11-18 18:15:16 +00:00
|
|
|
if port.TargetPort.IntValue() != redisPort {
|
2015-07-14 09:37:30 +00:00
|
|
|
Failf("Wrong target port: %d")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
By("exposing RC")
|
2015-11-08 18:58:21 +00:00
|
|
|
runKubectlOrDie("expose", "rc", "redis-master", "--name=rm2", "--port=1234", fmt.Sprintf("--target-port=%d", redisPort), nsFlag)
|
2015-09-16 23:20:40 +00:00
|
|
|
waitForService(c, ns, "rm2", true, poll, serviceStartTimeout)
|
|
|
|
validateService("rm2", 1234, serviceStartTimeout)
|
2015-07-14 09:37:30 +00:00
|
|
|
|
|
|
|
By("exposing service")
|
2015-11-08 18:58:21 +00:00
|
|
|
runKubectlOrDie("expose", "service", "rm2", "--name=rm3", "--port=2345", fmt.Sprintf("--target-port=%d", redisPort), nsFlag)
|
2015-09-16 23:20:40 +00:00
|
|
|
waitForService(c, ns, "rm3", true, poll, serviceStartTimeout)
|
|
|
|
validateService("rm3", 2345, serviceStartTimeout)
|
2015-07-14 09:37:30 +00:00
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2015-07-24 08:20:35 +00:00
|
|
|
Describe("Kubectl label", func() {
|
|
|
|
var podPath string
|
|
|
|
var nsFlag string
|
|
|
|
BeforeEach(func() {
|
|
|
|
podPath = filepath.Join(testContext.RepoRoot, "docs/user-guide/pod.yaml")
|
|
|
|
By("creating the pod")
|
|
|
|
nsFlag = fmt.Sprintf("--namespace=%v", ns)
|
2015-11-08 18:58:21 +00:00
|
|
|
runKubectlOrDie("create", "-f", podPath, nsFlag)
|
2015-07-24 08:20:35 +00:00
|
|
|
checkPodsRunningReady(c, ns, []string{simplePodName}, podStartTimeout)
|
|
|
|
})
|
|
|
|
AfterEach(func() {
|
|
|
|
cleanup(podPath, ns, simplePodSelector)
|
|
|
|
})
|
|
|
|
|
2015-10-07 14:09:24 +00:00
|
|
|
It("should update the label on a resource [Conformance]", func() {
|
2015-07-24 08:20:35 +00:00
|
|
|
labelName := "testing-label"
|
|
|
|
labelValue := "testing-label-value"
|
|
|
|
|
|
|
|
By("adding the label " + labelName + " with value " + labelValue + " to a pod")
|
2015-11-08 18:58:21 +00:00
|
|
|
runKubectlOrDie("label", "pods", simplePodName, labelName+"="+labelValue, nsFlag)
|
2015-07-24 08:20:35 +00:00
|
|
|
By("verifying the pod has the label " + labelName + " with the value " + labelValue)
|
2015-11-08 18:58:21 +00:00
|
|
|
output := runKubectlOrDie("get", "pod", simplePodName, "-L", labelName, nsFlag)
|
2015-07-24 08:20:35 +00:00
|
|
|
if !strings.Contains(output, labelValue) {
|
|
|
|
Failf("Failed updating label " + labelName + " to the pod " + simplePodName)
|
|
|
|
}
|
|
|
|
|
|
|
|
By("removing the label " + labelName + " of a pod")
|
2015-11-08 18:58:21 +00:00
|
|
|
runKubectlOrDie("label", "pods", simplePodName, labelName+"-", nsFlag)
|
2015-07-24 08:20:35 +00:00
|
|
|
By("verifying the pod doesn't have the label " + labelName)
|
2015-11-08 18:58:21 +00:00
|
|
|
output = runKubectlOrDie("get", "pod", simplePodName, "-L", labelName, nsFlag)
|
2015-07-24 08:20:35 +00:00
|
|
|
if strings.Contains(output, labelValue) {
|
|
|
|
Failf("Failed removing label " + labelName + " of the pod " + simplePodName)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2015-07-13 12:06:00 +00:00
|
|
|
Describe("Kubectl logs", func() {
|
2015-09-10 03:46:11 +00:00
|
|
|
var rcPath string
|
|
|
|
var nsFlag string
|
|
|
|
containerName := "redis-master"
|
|
|
|
BeforeEach(func() {
|
2015-07-13 12:06:00 +00:00
|
|
|
mkpath := func(file string) string {
|
|
|
|
return filepath.Join(testContext.RepoRoot, "examples/guestbook-go", file)
|
|
|
|
}
|
2015-09-10 03:46:11 +00:00
|
|
|
rcPath = mkpath("redis-master-controller.json")
|
|
|
|
By("creating an rc")
|
|
|
|
nsFlag = fmt.Sprintf("--namespace=%v", ns)
|
2015-11-08 18:58:21 +00:00
|
|
|
runKubectlOrDie("create", "-f", rcPath, nsFlag)
|
2015-09-10 03:46:11 +00:00
|
|
|
})
|
|
|
|
AfterEach(func() {
|
|
|
|
cleanup(rcPath, ns, simplePodSelector)
|
|
|
|
})
|
|
|
|
|
2015-10-07 14:09:24 +00:00
|
|
|
It("should be able to retrieve and filter logs [Conformance]", func() {
|
2015-07-13 12:06:00 +00:00
|
|
|
forEachPod(c, ns, "app", "redis", func(pod api.Pod) {
|
2015-09-10 03:46:11 +00:00
|
|
|
By("checking for a matching strings")
|
|
|
|
_, err := lookForStringInLog(ns, pod.Name, containerName, "The server is now ready to accept connections", podStartTimeout)
|
2015-07-13 12:06:00 +00:00
|
|
|
Expect(err).NotTo(HaveOccurred())
|
2015-09-10 03:46:11 +00:00
|
|
|
|
|
|
|
By("limiting log lines")
|
2015-11-08 18:58:21 +00:00
|
|
|
out := runKubectlOrDie("log", pod.Name, containerName, nsFlag, "--tail=1")
|
2015-09-10 03:46:11 +00:00
|
|
|
Expect(len(out)).NotTo(BeZero())
|
|
|
|
Expect(len(strings.Split(out, "\n"))).To(Equal(1))
|
|
|
|
|
|
|
|
By("limiting log bytes")
|
2015-11-08 18:58:21 +00:00
|
|
|
out = runKubectlOrDie("log", pod.Name, containerName, nsFlag, "--limit-bytes=1")
|
2015-09-10 03:46:11 +00:00
|
|
|
Expect(len(strings.Split(out, "\n"))).To(Equal(1))
|
|
|
|
Expect(len(out)).To(Equal(1))
|
|
|
|
|
|
|
|
By("exposing timestamps")
|
2015-11-08 18:58:21 +00:00
|
|
|
out = runKubectlOrDie("log", pod.Name, containerName, nsFlag, "--tail=1", "--timestamps")
|
2015-09-10 03:46:11 +00:00
|
|
|
lines := strings.Split(out, "\n")
|
|
|
|
Expect(len(lines)).To(Equal(1))
|
|
|
|
words := strings.Split(lines[0], " ")
|
|
|
|
Expect(len(words)).To(BeNumerically(">", 1))
|
|
|
|
if _, err := time.Parse(time.RFC3339Nano, words[0]); err != nil {
|
|
|
|
if _, err := time.Parse(time.RFC3339, words[0]); err != nil {
|
|
|
|
Failf("expected %q to be RFC3339 or RFC3339Nano", words[0])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
By("restricting to a time range")
|
|
|
|
time.Sleep(1500 * time.Millisecond) // ensure that startup logs on the node are seen as older than 1s
|
2015-11-08 18:58:21 +00:00
|
|
|
out = runKubectlOrDie("log", pod.Name, containerName, nsFlag, "--since=1s")
|
2015-09-10 03:46:11 +00:00
|
|
|
recent := len(strings.Split(out, "\n"))
|
2015-11-08 18:58:21 +00:00
|
|
|
out = runKubectlOrDie("log", pod.Name, containerName, nsFlag, "--since=24h")
|
2015-09-10 03:46:11 +00:00
|
|
|
older := len(strings.Split(out, "\n"))
|
|
|
|
Expect(recent).To(BeNumerically("<", older))
|
2015-07-13 12:06:00 +00:00
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
Describe("Kubectl patch", func() {
|
2015-10-07 14:09:24 +00:00
|
|
|
It("should add annotations for pods in rc [Conformance]", func() {
|
2015-07-13 12:06:00 +00:00
|
|
|
mkpath := func(file string) string {
|
|
|
|
return filepath.Join(testContext.RepoRoot, "examples/guestbook-go", file)
|
|
|
|
}
|
|
|
|
controllerJson := mkpath("redis-master-controller.json")
|
|
|
|
nsFlag := fmt.Sprintf("--namespace=%v", ns)
|
|
|
|
By("creating Redis RC")
|
2015-11-08 18:58:21 +00:00
|
|
|
runKubectlOrDie("create", "-f", controllerJson, nsFlag)
|
2015-07-13 12:06:00 +00:00
|
|
|
By("patching all pods")
|
|
|
|
forEachPod(c, ns, "app", "redis", func(pod api.Pod) {
|
2015-11-08 18:58:21 +00:00
|
|
|
runKubectlOrDie("patch", "pod", pod.Name, nsFlag, "-p", "{\"metadata\":{\"annotations\":{\"x\":\"y\"}}}")
|
2015-07-13 12:06:00 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
By("checking annotations")
|
|
|
|
forEachPod(c, ns, "app", "redis", func(pod api.Pod) {
|
|
|
|
found := false
|
|
|
|
for key, val := range pod.Annotations {
|
|
|
|
if key == "x" && val == "y" {
|
|
|
|
found = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !found {
|
2015-08-07 16:40:59 +00:00
|
|
|
Failf("Added annotation not found")
|
2015-07-13 12:06:00 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
2015-07-23 12:15:15 +00:00
|
|
|
|
|
|
|
Describe("Kubectl version", func() {
|
2015-10-07 14:09:24 +00:00
|
|
|
It("should check is all data is printed [Conformance]", func() {
|
2015-11-08 18:58:21 +00:00
|
|
|
version := runKubectlOrDie("version")
|
2015-07-23 12:15:15 +00:00
|
|
|
requiredItems := []string{"Client Version:", "Server Version:", "Major:", "Minor:", "GitCommit:"}
|
|
|
|
for _, item := range requiredItems {
|
|
|
|
if !strings.Contains(version, item) {
|
|
|
|
Failf("Required item %s not found in %s", item, version)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
2015-07-09 18:26:48 +00:00
|
|
|
|
2015-08-04 19:54:17 +00:00
|
|
|
Describe("Kubectl run rc", func() {
|
2015-07-09 18:26:48 +00:00
|
|
|
var nsFlag string
|
|
|
|
var rcName string
|
|
|
|
|
|
|
|
BeforeEach(func() {
|
|
|
|
nsFlag = fmt.Sprintf("--namespace=%v", ns)
|
|
|
|
rcName = "e2e-test-nginx-rc"
|
|
|
|
})
|
|
|
|
|
|
|
|
AfterEach(func() {
|
2015-11-08 18:58:21 +00:00
|
|
|
runKubectlOrDie("stop", "rc", rcName, nsFlag)
|
2015-07-09 18:26:48 +00:00
|
|
|
})
|
|
|
|
|
2015-10-07 14:09:24 +00:00
|
|
|
It("should create an rc from an image [Conformance]", func() {
|
2015-07-09 18:26:48 +00:00
|
|
|
image := "nginx"
|
|
|
|
|
|
|
|
By("running the image " + image)
|
2015-11-08 18:58:21 +00:00
|
|
|
runKubectlOrDie("run", rcName, "--image="+image, nsFlag)
|
2015-07-09 18:26:48 +00:00
|
|
|
By("verifying the rc " + rcName + " was created")
|
|
|
|
rc, err := c.ReplicationControllers(ns).Get(rcName)
|
|
|
|
if err != nil {
|
|
|
|
Failf("Failed getting rc %s: %v", rcName, err)
|
|
|
|
}
|
|
|
|
containers := rc.Spec.Template.Spec.Containers
|
|
|
|
if containers == nil || len(containers) != 1 || containers[0].Image != image {
|
|
|
|
Failf("Failed creating rc %s for 1 pod with expected image %s", rcName, image)
|
|
|
|
}
|
|
|
|
|
|
|
|
By("verifying the pod controlled by rc " + rcName + " was created")
|
|
|
|
label := labels.SelectorFromSet(labels.Set(map[string]string{"run": rcName}))
|
|
|
|
podlist, err := waitForPodsWithLabel(c, ns, label)
|
|
|
|
if err != nil {
|
|
|
|
Failf("Failed getting pod controlled by rc %s: %v", rcName, err)
|
|
|
|
}
|
|
|
|
pods := podlist.Items
|
|
|
|
if pods == nil || len(pods) != 1 || len(pods[0].Spec.Containers) != 1 || pods[0].Spec.Containers[0].Image != image {
|
2015-11-08 18:58:21 +00:00
|
|
|
runKubectlOrDie("get", "pods", "-L", "run", nsFlag)
|
2015-07-09 18:26:48 +00:00
|
|
|
Failf("Failed creating 1 pod with expected image %s. Number of pods = %v", image, len(pods))
|
|
|
|
}
|
|
|
|
})
|
2015-07-07 05:09:22 +00:00
|
|
|
|
|
|
|
})
|
|
|
|
|
2015-11-13 01:07:21 +00:00
|
|
|
Describe("Kubectl run job", func() {
|
2015-08-04 19:54:17 +00:00
|
|
|
var nsFlag string
|
2015-11-13 01:07:21 +00:00
|
|
|
var jobName string
|
2015-08-04 19:54:17 +00:00
|
|
|
|
|
|
|
BeforeEach(func() {
|
|
|
|
nsFlag = fmt.Sprintf("--namespace=%v", ns)
|
2015-11-13 01:07:21 +00:00
|
|
|
jobName = "e2e-test-nginx-job"
|
2015-08-04 19:54:17 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
AfterEach(func() {
|
2015-11-13 01:07:21 +00:00
|
|
|
runKubectlOrDie("stop", "jobs", jobName, nsFlag)
|
2015-08-04 19:54:17 +00:00
|
|
|
})
|
|
|
|
|
2015-11-13 01:07:21 +00:00
|
|
|
It("should create a job from an image when restart is OnFailure [Conformance]", func() {
|
2015-08-04 19:54:17 +00:00
|
|
|
image := "nginx"
|
|
|
|
|
|
|
|
By("running the image " + image)
|
2015-11-13 01:07:21 +00:00
|
|
|
runKubectlOrDie("run", jobName, "--restart=OnFailure", "--image="+image, nsFlag)
|
|
|
|
By("verifying the job " + jobName + " was created")
|
|
|
|
job, err := c.Extensions().Jobs(ns).Get(jobName)
|
2015-08-04 19:54:17 +00:00
|
|
|
if err != nil {
|
2015-11-13 01:07:21 +00:00
|
|
|
Failf("Failed getting job %s: %v", jobName, err)
|
2015-08-04 19:54:17 +00:00
|
|
|
}
|
2015-11-13 01:07:21 +00:00
|
|
|
containers := job.Spec.Template.Spec.Containers
|
2015-08-04 19:54:17 +00:00
|
|
|
if containers == nil || len(containers) != 1 || containers[0].Image != image {
|
2015-11-13 01:07:21 +00:00
|
|
|
Failf("Failed creating job %s for 1 pod with expected image %s", jobName, image)
|
2015-08-04 19:54:17 +00:00
|
|
|
}
|
2015-11-13 01:07:21 +00:00
|
|
|
if job.Spec.Template.Spec.RestartPolicy != api.RestartPolicyOnFailure {
|
|
|
|
Failf("Failed creating a job with correct restart policy for --restart=OnFailure")
|
2015-08-04 19:54:17 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2015-11-13 01:07:21 +00:00
|
|
|
It("should create a job from an image when restart is Never [Conformance]", func() {
|
2015-08-04 19:54:17 +00:00
|
|
|
image := "nginx"
|
|
|
|
|
|
|
|
By("running the image " + image)
|
2015-11-13 01:07:21 +00:00
|
|
|
runKubectlOrDie("run", jobName, "--restart=Never", "--image="+image, nsFlag)
|
|
|
|
By("verifying the job " + jobName + " was created")
|
|
|
|
job, err := c.Extensions().Jobs(ns).Get(jobName)
|
2015-08-04 19:54:17 +00:00
|
|
|
if err != nil {
|
2015-11-13 01:07:21 +00:00
|
|
|
Failf("Failed getting job %s: %v", jobName, err)
|
2015-08-04 19:54:17 +00:00
|
|
|
}
|
2015-11-13 01:07:21 +00:00
|
|
|
containers := job.Spec.Template.Spec.Containers
|
2015-08-04 19:54:17 +00:00
|
|
|
if containers == nil || len(containers) != 1 || containers[0].Image != image {
|
2015-11-13 01:07:21 +00:00
|
|
|
Failf("Failed creating job %s for 1 pod with expected image %s", jobName, image)
|
2015-08-04 19:54:17 +00:00
|
|
|
}
|
2015-11-13 01:07:21 +00:00
|
|
|
if job.Spec.Template.Spec.RestartPolicy != api.RestartPolicyNever {
|
|
|
|
Failf("Failed creating a job with correct restart policy for --restart=OnFailure")
|
2015-08-04 19:54:17 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
})
|
|
|
|
|
2015-07-07 05:09:22 +00:00
|
|
|
Describe("Proxy server", func() {
|
|
|
|
// TODO: test proxy options (static, prefix, etc)
|
2015-10-07 14:09:24 +00:00
|
|
|
It("should support proxy with --port 0 [Conformance]", func() {
|
2015-07-07 05:09:22 +00:00
|
|
|
By("starting the proxy server")
|
|
|
|
port, cmd, err := startProxyServer()
|
|
|
|
if cmd != nil {
|
|
|
|
defer tryKill(cmd)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
Failf("Failed to start proxy server: %v", err)
|
|
|
|
}
|
|
|
|
By("curling proxy /api/ output")
|
|
|
|
localAddr := fmt.Sprintf("http://localhost:%d/api/", port)
|
|
|
|
apiVersions, err := getAPIVersions(localAddr)
|
2015-10-09 05:18:16 +00:00
|
|
|
if err != nil {
|
|
|
|
Failf("Expected at least one supported apiversion, got error %v", err)
|
|
|
|
}
|
2015-07-07 05:09:22 +00:00
|
|
|
if len(apiVersions.Versions) < 1 {
|
|
|
|
Failf("Expected at least one supported apiversion, got %v", apiVersions)
|
|
|
|
}
|
|
|
|
})
|
2015-07-09 18:26:48 +00:00
|
|
|
|
2015-10-07 14:09:24 +00:00
|
|
|
It("should support --unix-socket=/path [Conformance]", func() {
|
2015-07-07 10:39:38 +00:00
|
|
|
By("Starting the proxy")
|
|
|
|
tmpdir, err := ioutil.TempDir("", "kubectl-proxy-unix")
|
|
|
|
if err != nil {
|
|
|
|
Failf("Failed to create temporary directory: %v", err)
|
|
|
|
}
|
|
|
|
path := filepath.Join(tmpdir, "test")
|
|
|
|
defer os.Remove(path)
|
|
|
|
defer os.Remove(tmpdir)
|
|
|
|
cmd := kubectlCmd("proxy", fmt.Sprintf("--unix-socket=%s", path))
|
|
|
|
stdout, stderr, err := startCmdAndStreamOutput(cmd)
|
|
|
|
if err != nil {
|
|
|
|
Failf("Failed to start kubectl command: %v", err)
|
|
|
|
}
|
|
|
|
defer stdout.Close()
|
|
|
|
defer stderr.Close()
|
|
|
|
defer tryKill(cmd)
|
|
|
|
buf := make([]byte, 128)
|
|
|
|
if _, err = stdout.Read(buf); err != nil {
|
|
|
|
Failf("Expected output from kubectl proxy: %v", err)
|
|
|
|
}
|
|
|
|
By("retrieving proxy /api/ output")
|
|
|
|
_, err = curlUnix("http://unused/api", path)
|
|
|
|
if err != nil {
|
|
|
|
Failf("Failed get of /api at %s: %v", path, err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
2015-02-20 17:35:42 +00:00
|
|
|
})
|
|
|
|
|
2015-07-24 08:20:35 +00:00
|
|
|
// Checks whether the output split by line contains the required elements.
|
|
|
|
func checkOutput(output string, required [][]string) {
|
|
|
|
outputLines := strings.Split(output, "\n")
|
|
|
|
currentLine := 0
|
|
|
|
for _, requirement := range required {
|
|
|
|
for currentLine < len(outputLines) && !strings.Contains(outputLines[currentLine], requirement[0]) {
|
|
|
|
currentLine++
|
|
|
|
}
|
|
|
|
if currentLine == len(outputLines) {
|
|
|
|
Failf("Failed to find %s in %s", requirement[0], output)
|
|
|
|
}
|
|
|
|
for _, item := range requirement[1:] {
|
|
|
|
if !strings.Contains(outputLines[currentLine], item) {
|
|
|
|
Failf("Failed to find %s in %s", item, outputLines[currentLine])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-09 01:30:38 +00:00
|
|
|
func getAPIVersions(apiEndpoint string) (*unversioned.APIVersions, error) {
|
2015-07-07 05:09:22 +00:00
|
|
|
body, err := curl(apiEndpoint)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("Failed http.Get of %s: %v", apiEndpoint, err)
|
|
|
|
}
|
2015-10-09 01:30:38 +00:00
|
|
|
var apiVersions unversioned.APIVersions
|
2015-07-07 05:09:22 +00:00
|
|
|
if err := json.Unmarshal([]byte(body), &apiVersions); err != nil {
|
|
|
|
return nil, fmt.Errorf("Failed to parse /api output %s: %v", body, err)
|
|
|
|
}
|
|
|
|
return &apiVersions, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func startProxyServer() (int, *exec.Cmd, error) {
|
|
|
|
// Specifying port 0 indicates we want the os to pick a random port.
|
|
|
|
cmd := kubectlCmd("proxy", "-p", "0")
|
|
|
|
stdout, stderr, err := startCmdAndStreamOutput(cmd)
|
|
|
|
if err != nil {
|
|
|
|
return -1, nil, err
|
|
|
|
}
|
|
|
|
defer stdout.Close()
|
|
|
|
defer stderr.Close()
|
|
|
|
buf := make([]byte, 128)
|
|
|
|
var n int
|
|
|
|
if n, err = stdout.Read(buf); err != nil {
|
|
|
|
return -1, cmd, fmt.Errorf("Failed to read from kubectl proxy stdout: %v", err)
|
|
|
|
}
|
|
|
|
output := string(buf[:n])
|
|
|
|
match := proxyRegexp.FindStringSubmatch(output)
|
|
|
|
if len(match) == 2 {
|
|
|
|
if port, err := strconv.Atoi(match[1]); err == nil {
|
|
|
|
return port, cmd, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1, cmd, fmt.Errorf("Failed to parse port from proxy stdout: %s", output)
|
|
|
|
}
|
|
|
|
|
2015-07-07 10:39:38 +00:00
|
|
|
func curlUnix(url string, path string) (string, error) {
|
|
|
|
dial := func(proto, addr string) (net.Conn, error) {
|
|
|
|
return net.Dial("unix", path)
|
|
|
|
}
|
|
|
|
transport := &http.Transport{
|
|
|
|
Dial: dial,
|
|
|
|
}
|
|
|
|
return curlTransport(url, transport)
|
|
|
|
}
|
|
|
|
|
|
|
|
func curlTransport(url string, transport *http.Transport) (string, error) {
|
|
|
|
client := &http.Client{Transport: transport}
|
|
|
|
resp, err := client.Get(url)
|
2015-06-30 16:43:37 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return string(body[:]), nil
|
|
|
|
}
|
|
|
|
|
2015-07-07 10:39:38 +00:00
|
|
|
func curl(url string) (string, error) {
|
|
|
|
return curlTransport(url, &http.Transport{})
|
|
|
|
}
|
|
|
|
|
2015-04-30 02:53:09 +00:00
|
|
|
func validateGuestbookApp(c *client.Client, ns string) {
|
2015-03-18 14:16:26 +00:00
|
|
|
Logf("Waiting for frontend to serve content.")
|
2015-04-30 02:53:09 +00:00
|
|
|
if !waitForGuestbookResponse(c, "get", "", `{"data": ""}`, guestbookStartupTimeout, ns) {
|
2015-03-18 14:16:26 +00:00
|
|
|
Failf("Frontend service did not start serving content in %v seconds.", guestbookStartupTimeout.Seconds())
|
2015-03-13 16:26:41 +00:00
|
|
|
}
|
|
|
|
|
2015-03-18 14:16:26 +00:00
|
|
|
Logf("Trying to add a new entry to the guestbook.")
|
2015-04-30 02:53:09 +00:00
|
|
|
if !waitForGuestbookResponse(c, "set", "TestEntry", `{"message": "Updated"}`, guestbookResponseTimeout, ns) {
|
2015-03-18 14:16:26 +00:00
|
|
|
Failf("Cannot added new entry in %v seconds.", guestbookResponseTimeout.Seconds())
|
2015-03-13 16:26:41 +00:00
|
|
|
}
|
|
|
|
|
2015-03-18 14:16:26 +00:00
|
|
|
Logf("Verifying that added entry can be retrieved.")
|
2015-04-30 02:53:09 +00:00
|
|
|
if !waitForGuestbookResponse(c, "get", "", `{"data": "TestEntry"}`, guestbookResponseTimeout, ns) {
|
2015-03-18 14:16:26 +00:00
|
|
|
Failf("Entry to guestbook wasn't correctly added in %v seconds.", guestbookResponseTimeout.Seconds())
|
2015-03-13 16:26:41 +00:00
|
|
|
}
|
2015-03-18 14:16:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Returns whether received expected response from guestbook on time.
|
2015-04-30 02:53:09 +00:00
|
|
|
func waitForGuestbookResponse(c *client.Client, cmd, arg, expectedResponse string, timeout time.Duration, ns string) bool {
|
2015-05-19 18:17:32 +00:00
|
|
|
for start := time.Now(); time.Since(start) < timeout; time.Sleep(5 * time.Second) {
|
2015-04-30 02:53:09 +00:00
|
|
|
res, err := makeRequestToGuestbook(c, cmd, arg, ns)
|
2015-03-18 14:16:26 +00:00
|
|
|
if err == nil && res == expectedResponse {
|
2015-05-19 18:17:32 +00:00
|
|
|
return true
|
2015-03-18 14:16:26 +00:00
|
|
|
}
|
2015-10-07 20:08:47 +00:00
|
|
|
Logf("Failed to get response from guestbook. err: %v, response: %s", err, res)
|
2015-05-19 18:17:32 +00:00
|
|
|
}
|
|
|
|
return false
|
2015-03-13 16:26:41 +00:00
|
|
|
}
|
|
|
|
|
2015-04-30 02:53:09 +00:00
|
|
|
func makeRequestToGuestbook(c *client.Client, cmd, value string, ns string) (string, error) {
|
2015-03-13 16:26:41 +00:00
|
|
|
result, err := c.Get().
|
|
|
|
Prefix("proxy").
|
2015-04-30 02:53:09 +00:00
|
|
|
Namespace(ns).
|
2015-03-13 16:26:41 +00:00
|
|
|
Resource("services").
|
|
|
|
Name("frontend").
|
2015-08-10 18:00:44 +00:00
|
|
|
Suffix("/guestbook.php").
|
2015-03-13 16:26:41 +00:00
|
|
|
Param("cmd", cmd).
|
|
|
|
Param("key", "messages").
|
|
|
|
Param("value", value).
|
|
|
|
Do().
|
|
|
|
Raw()
|
|
|
|
return string(result), err
|
|
|
|
}
|
|
|
|
|
2015-03-26 20:34:18 +00:00
|
|
|
type updateDemoData struct {
|
|
|
|
Image string
|
2015-03-26 19:22:04 +00:00
|
|
|
}
|
|
|
|
|
2015-09-10 21:32:57 +00:00
|
|
|
const applyTestLabel = "kubectl.kubernetes.io/apply-test"
|
|
|
|
|
|
|
|
func readBytesFromFile(filename string) []byte {
|
|
|
|
file, err := os.Open(filename)
|
|
|
|
if err != nil {
|
|
|
|
Failf(err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
data, err := ioutil.ReadAll(file)
|
|
|
|
if err != nil {
|
|
|
|
Failf(err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
return data
|
|
|
|
}
|
|
|
|
|
|
|
|
func readReplicationControllerFromFile(filename string) *api.ReplicationController {
|
|
|
|
data := readBytesFromFile(filename)
|
|
|
|
rc := api.ReplicationController{}
|
|
|
|
if err := yaml.Unmarshal(data, &rc); err != nil {
|
|
|
|
Failf(err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
return &rc
|
|
|
|
}
|
|
|
|
|
|
|
|
func modifyReplicationControllerConfiguration(filename string) io.Reader {
|
|
|
|
rc := readReplicationControllerFromFile(filename)
|
|
|
|
rc.Labels[applyTestLabel] = "ADDED"
|
|
|
|
rc.Spec.Selector[applyTestLabel] = "ADDED"
|
|
|
|
rc.Spec.Template.Labels[applyTestLabel] = "ADDED"
|
|
|
|
data, err := json.Marshal(rc)
|
|
|
|
if err != nil {
|
|
|
|
Failf("json marshal failed: %s\n", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return bytes.NewReader(data)
|
|
|
|
}
|
|
|
|
|
|
|
|
func forEachReplicationController(c *client.Client, ns, selectorKey, selectorValue string, fn func(api.ReplicationController)) {
|
|
|
|
var rcs *api.ReplicationControllerList
|
|
|
|
var err error
|
|
|
|
for t := time.Now(); time.Since(t) < podListTimeout; time.Sleep(poll) {
|
2015-11-26 10:06:01 +00:00
|
|
|
rcs, err = c.ReplicationControllers(ns).List(labels.SelectorFromSet(labels.Set(map[string]string{selectorKey: selectorValue})), fields.Everything(), unversioned.ListOptions{})
|
2015-09-10 21:32:57 +00:00
|
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
if len(rcs.Items) > 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if rcs == nil || len(rcs.Items) == 0 {
|
|
|
|
Failf("No replication controllers found")
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, rc := range rcs.Items {
|
|
|
|
fn(rc)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func validateReplicationControllerConfiguration(rc api.ReplicationController) {
|
|
|
|
if rc.Name == "redis-master" {
|
|
|
|
if _, ok := rc.Annotations[kubectl.LastAppliedConfigAnnotation]; !ok {
|
|
|
|
Failf("Annotation not found in modified configuration:\n%v\n", rc)
|
|
|
|
}
|
|
|
|
|
|
|
|
if value, ok := rc.Labels[applyTestLabel]; !ok || value != "ADDED" {
|
|
|
|
Failf("Added label %s not found in modified configuration:\n%v\n", applyTestLabel, rc)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-26 20:34:18 +00:00
|
|
|
// getUDData creates a validator function based on the input string (i.e. kitten.jpg).
|
2015-10-20 02:41:58 +00:00
|
|
|
// For example, if you send "kitten.jpg", this function verifies that the image jpg = kitten.jpg
|
2015-03-26 20:34:18 +00:00
|
|
|
// in the container's json field.
|
2015-04-30 02:53:09 +00:00
|
|
|
func getUDData(jpgExpected string, ns string) func(*client.Client, string) error {
|
2015-03-26 20:34:18 +00:00
|
|
|
|
|
|
|
// getUDData validates data.json in the update-demo (returns nil if data is ok).
|
|
|
|
return func(c *client.Client, podID string) error {
|
|
|
|
Logf("validating pod %s", podID)
|
|
|
|
body, err := c.Get().
|
2015-04-30 02:53:09 +00:00
|
|
|
Namespace(ns).
|
2015-03-26 20:34:18 +00:00
|
|
|
Resource("pods").
|
2015-11-09 07:23:48 +00:00
|
|
|
SubResource("proxy").
|
2015-03-26 20:34:18 +00:00
|
|
|
Name(podID).
|
|
|
|
Suffix("data.json").
|
|
|
|
Do().
|
|
|
|
Raw()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2015-03-26 19:22:04 +00:00
|
|
|
}
|
2015-03-26 20:34:18 +00:00
|
|
|
Logf("got data: %s", body)
|
|
|
|
var data updateDemoData
|
|
|
|
if err := json.Unmarshal(body, &data); err != nil {
|
|
|
|
return err
|
2015-03-26 19:22:04 +00:00
|
|
|
}
|
2015-03-26 20:34:18 +00:00
|
|
|
Logf("Unmarshalled json jpg/img => %s , expecting %s .", data, jpgExpected)
|
|
|
|
if strings.Contains(data.Image, jpgExpected) {
|
|
|
|
return nil
|
|
|
|
} else {
|
2015-08-20 02:09:57 +00:00
|
|
|
return errors.New(fmt.Sprintf("data served up in container is inaccurate, %s didn't contain %s", data, jpgExpected))
|
2015-03-26 19:22:04 +00:00
|
|
|
}
|
|
|
|
}
|
2015-02-20 17:35:42 +00:00
|
|
|
}
|
2015-08-31 17:23:47 +00:00
|
|
|
|
|
|
|
// newBlockingReader returns a reader that allows reading the given string,
|
|
|
|
// then blocks until Close() is called on the returned closer.
|
|
|
|
//
|
|
|
|
// We're explicitly returning the reader and closer separately, because
|
|
|
|
// the closer needs to be the *os.File we get from os.Pipe(). This is required
|
|
|
|
// so the exec of kubectl can pass the underlying file descriptor to the exec
|
|
|
|
// syscall, instead of creating another os.Pipe and blocking on the io.Copy
|
|
|
|
// between the source (e.g. stdin) and the write half of the pipe.
|
|
|
|
func newBlockingReader(s string) (io.Reader, io.Closer, error) {
|
|
|
|
r, w, err := os.Pipe()
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
w.Write([]byte(s))
|
|
|
|
return r, w, nil
|
|
|
|
}
|
2015-09-17 20:11:27 +00:00
|
|
|
|
|
|
|
// newStreamingUpload creates a new http.Request that will stream POST
|
|
|
|
// a file to a URI.
|
|
|
|
func newStreamingUpload(filePath string) (*io.PipeReader, *multipart.Writer, error) {
|
|
|
|
file, err := os.Open(filePath)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
r, w := io.Pipe()
|
|
|
|
|
|
|
|
postBodyWriter := multipart.NewWriter(w)
|
|
|
|
|
|
|
|
go streamingUpload(file, filepath.Base(filePath), postBodyWriter, w)
|
|
|
|
return r, postBodyWriter, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// streamingUpload streams a file via a pipe through a multipart.Writer.
|
|
|
|
// Generally one should use newStreamingUpload instead of calling this directly.
|
|
|
|
func streamingUpload(file *os.File, fileName string, postBodyWriter *multipart.Writer, w *io.PipeWriter) {
|
|
|
|
defer GinkgoRecover()
|
|
|
|
defer file.Close()
|
|
|
|
defer w.Close()
|
|
|
|
|
|
|
|
// Set up the form file
|
|
|
|
fileWriter, err := postBodyWriter.CreateFormFile("file", fileName)
|
|
|
|
if err != nil {
|
|
|
|
Failf("Unable to to write file at %s to buffer. Error: %s", fileName, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy kubectl binary into the file writer
|
|
|
|
if _, err := io.Copy(fileWriter, file); err != nil {
|
|
|
|
Failf("Unable to to copy file at %s into the file writer. Error: %s", fileName, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Nothing more should be written to this instance of the postBodyWriter
|
|
|
|
if err := postBodyWriter.Close(); err != nil {
|
|
|
|
Failf("Unable to close the writer for file upload. Error: %s", err)
|
|
|
|
}
|
|
|
|
}
|
2015-11-02 17:58:50 +00:00
|
|
|
|
|
|
|
var binPrefixes = []string{
|
|
|
|
"_output/dockerized/bin",
|
|
|
|
"_output/local/bin",
|
|
|
|
"platforms",
|
|
|
|
}
|
|
|
|
|
|
|
|
// findBinary searches through likely paths to find the specified binary. It
|
|
|
|
// takes the one that has been built most recently. Platform should be
|
|
|
|
// specified as '<os>/<arch>'. For example: 'linux/amd64'.
|
|
|
|
func findBinary(binName string, platform string) (string, error) {
|
|
|
|
var binTime time.Time
|
|
|
|
var binPath string
|
|
|
|
|
|
|
|
for _, pre := range binPrefixes {
|
|
|
|
tryPath := path.Join(testContext.RepoRoot, pre, platform, binName)
|
|
|
|
fi, err := os.Stat(tryPath)
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if fi.ModTime().After(binTime) {
|
|
|
|
binPath = tryPath
|
|
|
|
binTime = fi.ModTime()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(binPath) > 0 {
|
|
|
|
return binPath, nil
|
|
|
|
}
|
|
|
|
return binPath, fmt.Errorf("Could not find %v for %v", binName, platform)
|
|
|
|
}
|