From 0003d5d983c1386a697dab19b77d520354dbc840 Mon Sep 17 00:00:00 2001 From: Johan Euphrosine Date: Mon, 22 Dec 2014 13:13:47 -0800 Subject: [PATCH 01/50] contrib: add git-sync container --- contrib/git-sync/Dockerfile | 4 +++ contrib/git-sync/README.md | 16 +++++++++ contrib/git-sync/main.go | 72 +++++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 contrib/git-sync/Dockerfile create mode 100644 contrib/git-sync/README.md create mode 100644 contrib/git-sync/main.go diff --git a/contrib/git-sync/Dockerfile b/contrib/git-sync/Dockerfile new file mode 100644 index 00000000000..2c5596c7b90 --- /dev/null +++ b/contrib/git-sync/Dockerfile @@ -0,0 +1,4 @@ +FROM golang:1.4-onbuild +VOLUME ["/git"] +CMD ["-dest", "/git"] +ENTRYPOINT ["/go/bin/git-sync"] diff --git a/contrib/git-sync/README.md b/contrib/git-sync/README.md new file mode 100644 index 00000000000..a3059d91cd6 --- /dev/null +++ b/contrib/git-sync/README.md @@ -0,0 +1,16 @@ +# git-sync + +git-sync is a command that periodically sync a git repository to a local directory. + +It can be used to source a container volume with the content of a git repo. + +## Usage + +``` +# build the container +docker build -t git-sync . +# run the git-sync container +docker run -d -e INTERVAL=1s -e REPO=https://github.com/GoogleCloudPlatform/kubernetes -e BRANCH=gh-pages -v /git-data:/usr/share/nginx/html git-sync +# run a nginx container to serve sync'ed content +docker run -d -p 8080:80 -v /git-data:/var/www nginx +``` diff --git a/contrib/git-sync/main.go b/contrib/git-sync/main.go new file mode 100644 index 00000000000..2d53e259dce --- /dev/null +++ b/contrib/git-sync/main.go @@ -0,0 +1,72 @@ +package main // import "github.com/GoogleCloudPlatform/kubernetes/git-sync" + +import ( + "flag" + "log" + "net/http" + "os" + "os/exec" + "path" + "strings" + "time" +) + +var interval = flag.String("interval", env("INTERVAL", "60s"), "git pull interval") +var repo = flag.String("repo", env("REPO", ""), "git repo url") +var branch = flag.String("branch", env("BRANCH", "master"), "git branch") +var hook = flag.String("hook", env("HOOK", "/"), "web hook path") +var dest = flag.String("dest", env("DEST", ""), "destination path") + +func env(key, def string) string { + if env := os.Getenv(key); env != "" { + return env + } + return def +} + +const usage = "usage: REPO= DEST= [INTERVAL= BRANCH= HOOK=] git-sync -repo GIT_REPO_URL -dest PATH [-interval -branch -hook]" + +func main() { + flag.Parse() + if *repo == "" || *dest == "" { + flag.Usage() + log.Fatal(usage) + } + pullInterval, err := time.ParseDuration(*interval) + if err != nil { + log.Fatalf("error parsing time duration %q: %v", *interval, err) + } + if _, err := exec.LookPath("git"); err != nil { + log.Fatalf("required git executable not found: %v", err) + } + go func() { + for _ = range time.Tick(pullInterval) { + gitSync() + } + }() + http.HandleFunc(*hook, func(w http.ResponseWriter, r *http.Request) { + gitSync() + }) + log.Fatal(http.ListenAndServe(":8080", nil)) +} + +func gitSync() { + if _, err := os.Stat(path.Join(*dest, ".git")); os.IsNotExist(err) { + cmd := exec.Command("git", "clone", "-b", *branch, *repo, *dest) + output, err := cmd.CombinedOutput() + if err != nil { + log.Printf("command %q : %v", strings.Join(cmd.Args, " "), err) + return + } + log.Println(string(output)) + return + } + cmd := exec.Command("git", "pull", "origin", *branch) + cmd.Dir = *dest + output, err := cmd.CombinedOutput() + if err != nil { + log.Printf("command %q : %v", strings.Join(cmd.Args, " "), err) + return + } + log.Println(string(output)) +} From d67db4ecfa6f5648909fdddf719af42368208480 Mon Sep 17 00:00:00 2001 From: Johan Euphrosine Date: Mon, 22 Dec 2014 17:34:56 -0800 Subject: [PATCH 02/50] config/git-sync: add envvar prefix, fix README --- contrib/git-sync/Dockerfile | 2 +- contrib/git-sync/README.md | 4 ++-- contrib/git-sync/main.go | 14 +++++++------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/contrib/git-sync/Dockerfile b/contrib/git-sync/Dockerfile index 2c5596c7b90..6929d15b454 100644 --- a/contrib/git-sync/Dockerfile +++ b/contrib/git-sync/Dockerfile @@ -1,4 +1,4 @@ FROM golang:1.4-onbuild VOLUME ["/git"] -CMD ["-dest", "/git"] +ENV GIT_SYNC_DEST /git ENTRYPOINT ["/go/bin/git-sync"] diff --git a/contrib/git-sync/README.md b/contrib/git-sync/README.md index a3059d91cd6..1ec2d997066 100644 --- a/contrib/git-sync/README.md +++ b/contrib/git-sync/README.md @@ -10,7 +10,7 @@ It can be used to source a container volume with the content of a git repo. # build the container docker build -t git-sync . # run the git-sync container -docker run -d -e INTERVAL=1s -e REPO=https://github.com/GoogleCloudPlatform/kubernetes -e BRANCH=gh-pages -v /git-data:/usr/share/nginx/html git-sync +docker run -d -e GIT_SYNC_INTERVAL=1s -e GIT_SYNC_REPO=https://github.com/GoogleCloudPlatform/kubernetes -e GIT_SYNC_BRANCH=gh-pages -v /git-data:/git git-sync # run a nginx container to serve sync'ed content -docker run -d -p 8080:80 -v /git-data:/var/www nginx +docker run -d -p 8080:80 -v /git-data:/usr/share/nginx/html nginx ``` diff --git a/contrib/git-sync/main.go b/contrib/git-sync/main.go index 2d53e259dce..4b7cbf2f1be 100644 --- a/contrib/git-sync/main.go +++ b/contrib/git-sync/main.go @@ -11,11 +11,11 @@ import ( "time" ) -var interval = flag.String("interval", env("INTERVAL", "60s"), "git pull interval") -var repo = flag.String("repo", env("REPO", ""), "git repo url") -var branch = flag.String("branch", env("BRANCH", "master"), "git branch") -var hook = flag.String("hook", env("HOOK", "/"), "web hook path") -var dest = flag.String("dest", env("DEST", ""), "destination path") +var interval = flag.String("interval", env("GIT_SYNC_INTERVAL", "60s"), "git pull interval") +var repo = flag.String("repo", env("GIT_SYNC_REPO", ""), "git repo url") +var branch = flag.String("branch", env("GIT_SYNC_BRANCH", "master"), "git branch") +var handler = flag.String("handler", env("GIT_SYNC_HANDLER", "/"), "web hook handler") +var dest = flag.String("dest", env("GIT_SYNC_DEST", ""), "destination path") func env(key, def string) string { if env := os.Getenv(key); env != "" { @@ -24,7 +24,7 @@ func env(key, def string) string { return def } -const usage = "usage: REPO= DEST= [INTERVAL= BRANCH= HOOK=] git-sync -repo GIT_REPO_URL -dest PATH [-interval -branch -hook]" +const usage = "usage: GIT_SYNC_REPO= GIT_SYNC_DEST= [GIT_SYNC_INTERVAL= GIT_SYNC_BRANCH= GIT_SYNC_HANDLER=] git-sync -repo GIT_REPO_URL -dest PATH [-interval -branch -handler]" func main() { flag.Parse() @@ -44,7 +44,7 @@ func main() { gitSync() } }() - http.HandleFunc(*hook, func(w http.ResponseWriter, r *http.Request) { + http.HandleFunc(*handler, func(w http.ResponseWriter, r *http.Request) { gitSync() }) log.Fatal(http.ListenAndServe(":8080", nil)) From 9f37c9d4112a0311f725137b6cd0fba5db4c394d Mon Sep 17 00:00:00 2001 From: Johan Euphrosine Date: Tue, 23 Dec 2014 13:54:29 -0800 Subject: [PATCH 03/50] contrib/git-sync: add atomic checkout --- contrib/git-sync/README.md | 2 +- contrib/git-sync/main.go | 107 +++++++++++++++++++++++++++++++------ 2 files changed, 91 insertions(+), 18 deletions(-) diff --git a/contrib/git-sync/README.md b/contrib/git-sync/README.md index 1ec2d997066..52bf2c655d9 100644 --- a/contrib/git-sync/README.md +++ b/contrib/git-sync/README.md @@ -12,5 +12,5 @@ docker build -t git-sync . # run the git-sync container docker run -d -e GIT_SYNC_INTERVAL=1s -e GIT_SYNC_REPO=https://github.com/GoogleCloudPlatform/kubernetes -e GIT_SYNC_BRANCH=gh-pages -v /git-data:/git git-sync # run a nginx container to serve sync'ed content -docker run -d -p 8080:80 -v /git-data:/usr/share/nginx/html nginx +docker run -d -p 8080:80 -v /git-data/HEAD:/usr/share/nginx/html nginx ``` diff --git a/contrib/git-sync/main.go b/contrib/git-sync/main.go index 4b7cbf2f1be..3060fb71e03 100644 --- a/contrib/git-sync/main.go +++ b/contrib/git-sync/main.go @@ -2,6 +2,7 @@ package main // import "github.com/GoogleCloudPlatform/kubernetes/git-sync" import ( "flag" + "fmt" "log" "net/http" "os" @@ -39,34 +40,106 @@ func main() { if _, err := exec.LookPath("git"); err != nil { log.Fatalf("required git executable not found: %v", err) } + repo, err := NewRepo() + if err != nil { + log.Fatalf("error creating repo: %v", err) + } + syncc := make(chan struct{}) + tick := time.Tick(pullInterval) go func() { - for _ = range time.Tick(pullInterval) { - gitSync() + for { + repo.Sync() + select { + case <-tick: + case <-syncc: + } } }() http.HandleFunc(*handler, func(w http.ResponseWriter, r *http.Request) { - gitSync() + syncc <- struct{}{} }) log.Fatal(http.ListenAndServe(":8080", nil)) } -func gitSync() { - if _, err := os.Stat(path.Join(*dest, ".git")); os.IsNotExist(err) { - cmd := exec.Command("git", "clone", "-b", *branch, *repo, *dest) - output, err := cmd.CombinedOutput() - if err != nil { - log.Printf("command %q : %v", strings.Join(cmd.Args, " "), err) - return - } - log.Println(string(output)) - return +type Repo struct { + basePath string + mirrorPath string + lastRev string +} + +func NewRepo() (*Repo, error) { + mirrorRepoPath := path.Join(*dest, ".git") + _, err := os.Stat(mirrorRepoPath) + if err == nil { + log.Printf("found existing mirror repo %q", mirrorRepoPath) + return &Repo{ + basePath: *dest, + mirrorPath: mirrorRepoPath, + }, nil } - cmd := exec.Command("git", "pull", "origin", *branch) - cmd.Dir = *dest + if !os.IsNotExist(err) { + return nil, fmt.Errorf("error checking repo %q: %v", mirrorRepoPath, err) + } + cmd := exec.Command("git", "clone", "--mirror", "-b", *branch, *repo, mirrorRepoPath) output, err := cmd.CombinedOutput() if err != nil { - log.Printf("command %q : %v", strings.Join(cmd.Args, " "), err) + return nil, fmt.Errorf("error cloning repo %q: %v:", strings.Join(cmd.Args, " "), err) + } + log.Printf("clone %q: %s", *repo, string(output)) + return &Repo{ + basePath: *dest, + mirrorPath: mirrorRepoPath, + }, nil +} + +func (r *Repo) Sync() { + cmd := exec.Command("git", "fetch", "origin", *branch) + cmd.Dir = r.mirrorPath + output, err := cmd.CombinedOutput() + if err != nil { + log.Printf("error running command %q: %v", strings.Join(cmd.Args, " "), err) + return + } + log.Printf("fetch: %s", string(output)) + cmd = exec.Command("git", "rev-parse", "HEAD") + cmd.Dir = r.mirrorPath + output, err = cmd.CombinedOutput() + if err != nil { + log.Printf("error running command %q: %v", strings.Join(cmd.Args, " "), err) + return + } + rev := strings.TrimSpace(string(output)) + if rev == r.lastRev { + log.Printf("no new rev since last check %q", rev) + return + } + r.lastRev = rev + log.Printf("HEAD is: %q", rev) + repoPath := path.Join(r.basePath, rev) + _, err = os.Stat(repoPath) + if err == nil { + log.Printf("found existing repo: %q", repoPath) + return + } + if !os.IsNotExist(err) { + log.Printf("error stating repo %q: %v", repoPath, err) + return + } + cmd = exec.Command("git", "clone", r.mirrorPath, repoPath) + output, err = cmd.CombinedOutput() + if err != nil { + log.Printf("error running command %q : %v", strings.Join(cmd.Args, " "), err) + return + } + log.Printf("clone %q: %v", repoPath, string(output)) + tempPath := path.Join(r.basePath, "HEAD."+rev) + if err := os.Symlink(rev, tempPath); err != nil { + log.Printf("error creating temporary symlink %q: %v", tempPath, err) + return + } + linkPath := path.Join(r.basePath, "HEAD") + if err := os.Rename(tempPath, linkPath); err != nil { + log.Printf("error moving symlink %q: %v", linkPath, err) return } - log.Println(string(output)) } From 79dd775d72110df443630a2ae1a79b5d350382de Mon Sep 17 00:00:00 2001 From: Johan Euphrosine Date: Tue, 23 Dec 2014 15:21:51 -0800 Subject: [PATCH 04/50] contrib/git-sync: add comment --- contrib/git-sync/main.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/contrib/git-sync/main.go b/contrib/git-sync/main.go index 3060fb71e03..b0aff6179b5 100644 --- a/contrib/git-sync/main.go +++ b/contrib/git-sync/main.go @@ -1,3 +1,21 @@ +/* +Copyright 2014 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// git-sync is a command that periodically sync a git repository to a local directory. + package main // import "github.com/GoogleCloudPlatform/kubernetes/git-sync" import ( @@ -67,6 +85,7 @@ type Repo struct { lastRev string } +// NewRepo initalize a local bare repository mirror. func NewRepo() (*Repo, error) { mirrorRepoPath := path.Join(*dest, ".git") _, err := os.Stat(mirrorRepoPath) @@ -92,6 +111,9 @@ func NewRepo() (*Repo, error) { }, nil } +// Sync fetch new revision from the origin remote in the bare repository +// create a new checkout named after the revision +// update HEAD symlink to point to the new revision func (r *Repo) Sync() { cmd := exec.Command("git", "fetch", "origin", *branch) cmd.Dir = r.mirrorPath From 0415b63ab49310a23b9296e65447fbb297cbb099 Mon Sep 17 00:00:00 2001 From: Johan Euphrosine Date: Thu, 29 Jan 2015 15:39:51 -0800 Subject: [PATCH 05/50] contrib/git-sync: remove sync loop, simplify logic --- contrib/git-sync/README.md | 6 +- contrib/git-sync/main.go | 149 ++++++++++--------------------------- 2 files changed, 41 insertions(+), 114 deletions(-) diff --git a/contrib/git-sync/README.md b/contrib/git-sync/README.md index 52bf2c655d9..f8faa4a0cd1 100644 --- a/contrib/git-sync/README.md +++ b/contrib/git-sync/README.md @@ -1,6 +1,6 @@ # git-sync -git-sync is a command that periodically sync a git repository to a local directory. +git-sync is a command that pull a git repository to a local directory. It can be used to source a container volume with the content of a git repo. @@ -10,7 +10,7 @@ It can be used to source a container volume with the content of a git repo. # build the container docker build -t git-sync . # run the git-sync container -docker run -d -e GIT_SYNC_INTERVAL=1s -e GIT_SYNC_REPO=https://github.com/GoogleCloudPlatform/kubernetes -e GIT_SYNC_BRANCH=gh-pages -v /git-data:/git git-sync +docker run -d GIT_SYNC_REPO=https://github.com/GoogleCloudPlatform/kubernetes -e GIT_SYNC_BRANCH=gh-pages -r HEAD -v /git-data:/git git-sync # run a nginx container to serve sync'ed content -docker run -d -p 8080:80 -v /git-data/HEAD:/usr/share/nginx/html nginx +docker run -d -p 8080:80 -v /git-data:/usr/share/nginx/html nginx ``` diff --git a/contrib/git-sync/main.go b/contrib/git-sync/main.go index b0aff6179b5..355c0e1bc7e 100644 --- a/contrib/git-sync/main.go +++ b/contrib/git-sync/main.go @@ -14,27 +14,24 @@ See the License for the specific language governing permissions and limitations under the License. */ -// git-sync is a command that periodically sync a git repository to a local directory. +// git-sync is a command that pull a git repository to a local directory. -package main // import "github.com/GoogleCloudPlatform/kubernetes/git-sync" +package main // import "github.com/GoogleCloudPlatform/kubernetes/contrib/git-sync" import ( "flag" "fmt" "log" - "net/http" "os" "os/exec" "path" "strings" - "time" ) -var interval = flag.String("interval", env("GIT_SYNC_INTERVAL", "60s"), "git pull interval") -var repo = flag.String("repo", env("GIT_SYNC_REPO", ""), "git repo url") -var branch = flag.String("branch", env("GIT_SYNC_BRANCH", "master"), "git branch") -var handler = flag.String("handler", env("GIT_SYNC_HANDLER", "/"), "web hook handler") -var dest = flag.String("dest", env("GIT_SYNC_DEST", ""), "destination path") +var flRepo = flag.String("repo", env("GIT_SYNC_REPO", ""), "git repo url") +var flBranch = flag.String("branch", env("GIT_SYNC_BRANCH", "master"), "git branch") +var flRev = flag.String("rev", env("GIT_SYNC_BRANCH", "HEAD"), "git rev") +var flDest = flag.String("dest", env("GIT_SYNC_DEST", ""), "destination path") func env(key, def string) string { if env := os.Getenv(key); env != "" { @@ -43,125 +40,55 @@ func env(key, def string) string { return def } -const usage = "usage: GIT_SYNC_REPO= GIT_SYNC_DEST= [GIT_SYNC_INTERVAL= GIT_SYNC_BRANCH= GIT_SYNC_HANDLER=] git-sync -repo GIT_REPO_URL -dest PATH [-interval -branch -handler]" +const usage = "usage: GIT_SYNC_REPO= GIT_SYNC_DEST= [GIT_SYNC_BRANCH=] git-sync -repo GIT_REPO_URL -dest PATH [-branch]" func main() { flag.Parse() - if *repo == "" || *dest == "" { + if *flRepo == "" || *flDest == "" { flag.Usage() log.Fatal(usage) } - pullInterval, err := time.ParseDuration(*interval) - if err != nil { - log.Fatalf("error parsing time duration %q: %v", *interval, err) - } if _, err := exec.LookPath("git"); err != nil { log.Fatalf("required git executable not found: %v", err) } - repo, err := NewRepo() - if err != nil { - log.Fatalf("error creating repo: %v", err) + if err := syncRepo(*flRepo, *flDest, *flBranch, *flRev); err != nil { + log.Fatalf("error syncing repo: %v", err) } - syncc := make(chan struct{}) - tick := time.Tick(pullInterval) - go func() { - for { - repo.Sync() - select { - case <-tick: - case <-syncc: - } +} + +// syncRepo syncs the branch of a given repository to the destination at the given rev. +func syncRepo(repo, dest, branch, rev string) error { + gitRepoPath := path.Join(dest, ".git") + _, err := os.Stat(gitRepoPath) + switch { + case os.IsNotExist(err): + // clone repo + cmd := exec.Command("git", "clone", "--no-checkout", "-b", branch, repo, dest) + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("error cloning repo %q: %v: %s", strings.Join(cmd.Args, " "), err, string(output)) } - }() - http.HandleFunc(*handler, func(w http.ResponseWriter, r *http.Request) { - syncc <- struct{}{} - }) - log.Fatal(http.ListenAndServe(":8080", nil)) -} - -type Repo struct { - basePath string - mirrorPath string - lastRev string -} - -// NewRepo initalize a local bare repository mirror. -func NewRepo() (*Repo, error) { - mirrorRepoPath := path.Join(*dest, ".git") - _, err := os.Stat(mirrorRepoPath) - if err == nil { - log.Printf("found existing mirror repo %q", mirrorRepoPath) - return &Repo{ - basePath: *dest, - mirrorPath: mirrorRepoPath, - }, nil + log.Printf("clone %q: %s", repo, string(output)) + case err != nil: + return fmt.Errorf("error checking if repo exist %q: %v", gitRepoPath, err) } - if !os.IsNotExist(err) { - return nil, fmt.Errorf("error checking repo %q: %v", mirrorRepoPath, err) - } - cmd := exec.Command("git", "clone", "--mirror", "-b", *branch, *repo, mirrorRepoPath) + + // fetch branch + cmd := exec.Command("git", "fetch", "origin", branch) + cmd.Dir = dest output, err := cmd.CombinedOutput() if err != nil { - return nil, fmt.Errorf("error cloning repo %q: %v:", strings.Join(cmd.Args, " "), err) + return fmt.Errorf("error running command %q: %v: %s", strings.Join(cmd.Args, " "), err, string(output)) } - log.Printf("clone %q: %s", *repo, string(output)) - return &Repo{ - basePath: *dest, - mirrorPath: mirrorRepoPath, - }, nil -} + log.Printf("fetch %q: %s", branch, string(output)) -// Sync fetch new revision from the origin remote in the bare repository -// create a new checkout named after the revision -// update HEAD symlink to point to the new revision -func (r *Repo) Sync() { - cmd := exec.Command("git", "fetch", "origin", *branch) - cmd.Dir = r.mirrorPath - output, err := cmd.CombinedOutput() - if err != nil { - log.Printf("error running command %q: %v", strings.Join(cmd.Args, " "), err) - return - } - log.Printf("fetch: %s", string(output)) - cmd = exec.Command("git", "rev-parse", "HEAD") - cmd.Dir = r.mirrorPath + // reset working copy + cmd = exec.Command("git", "reset", "--hard", rev) + cmd.Dir = dest output, err = cmd.CombinedOutput() if err != nil { - log.Printf("error running command %q: %v", strings.Join(cmd.Args, " "), err) - return - } - rev := strings.TrimSpace(string(output)) - if rev == r.lastRev { - log.Printf("no new rev since last check %q", rev) - return - } - r.lastRev = rev - log.Printf("HEAD is: %q", rev) - repoPath := path.Join(r.basePath, rev) - _, err = os.Stat(repoPath) - if err == nil { - log.Printf("found existing repo: %q", repoPath) - return - } - if !os.IsNotExist(err) { - log.Printf("error stating repo %q: %v", repoPath, err) - return - } - cmd = exec.Command("git", "clone", r.mirrorPath, repoPath) - output, err = cmd.CombinedOutput() - if err != nil { - log.Printf("error running command %q : %v", strings.Join(cmd.Args, " "), err) - return - } - log.Printf("clone %q: %v", repoPath, string(output)) - tempPath := path.Join(r.basePath, "HEAD."+rev) - if err := os.Symlink(rev, tempPath); err != nil { - log.Printf("error creating temporary symlink %q: %v", tempPath, err) - return - } - linkPath := path.Join(r.basePath, "HEAD") - if err := os.Rename(tempPath, linkPath); err != nil { - log.Printf("error moving symlink %q: %v", linkPath, err) - return + return fmt.Errorf("error running command %q : %v: %s", strings.Join(cmd.Args, " "), err, string(output)) } + log.Printf("reset %q: %v", rev, string(output)) + return nil } From 5aba5f00c0cf7dbd1b54083d559a49e47f959c53 Mon Sep 17 00:00:00 2001 From: Johan Euphrosine Date: Thu, 29 Jan 2015 23:21:49 -0800 Subject: [PATCH 06/50] contrib/git-sync: add wait --- contrib/git-sync/main.go | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/contrib/git-sync/main.go b/contrib/git-sync/main.go index 355c0e1bc7e..7667e8df7a5 100644 --- a/contrib/git-sync/main.go +++ b/contrib/git-sync/main.go @@ -25,22 +25,37 @@ import ( "os" "os/exec" "path" + "strconv" "strings" + "time" ) -var flRepo = flag.String("repo", env("GIT_SYNC_REPO", ""), "git repo url") -var flBranch = flag.String("branch", env("GIT_SYNC_BRANCH", "master"), "git branch") -var flRev = flag.String("rev", env("GIT_SYNC_BRANCH", "HEAD"), "git rev") -var flDest = flag.String("dest", env("GIT_SYNC_DEST", ""), "destination path") +var flRepo = flag.String("repo", envString("GIT_SYNC_REPO", ""), "git repo url") +var flBranch = flag.String("branch", envString("GIT_SYNC_BRANCH", "master"), "git branch") +var flRev = flag.String("rev", envString("GIT_SYNC_BRANCH", "HEAD"), "git rev") +var flDest = flag.String("dest", envString("GIT_SYNC_DEST", ""), "destination path") +var flWait = flag.Int("wait", envInt("GIT_SYNC_WAIT", 0), "number of seconds to wait before exit") -func env(key, def string) string { +func envString(key, def string) string { if env := os.Getenv(key); env != "" { return env } return def } -const usage = "usage: GIT_SYNC_REPO= GIT_SYNC_DEST= [GIT_SYNC_BRANCH=] git-sync -repo GIT_REPO_URL -dest PATH [-branch]" +func envInt(key string, def int) int { + if env := os.Getenv(key); env != "" { + val, err := strconv.Atoi(env) + if err != nil { + log.Println("invalid value for %q: using default: %q", key, def) + return def + } + return val + } + return def +} + +const usage = "usage: GIT_SYNC_REPO= GIT_SYNC_DEST= [GIT_SYNC_BRANCH= GIT_SYNC_WAIT=] git-sync -repo GIT_REPO_URL -dest PATH [-branch -wait]" func main() { flag.Parse() @@ -54,6 +69,9 @@ func main() { if err := syncRepo(*flRepo, *flDest, *flBranch, *flRev); err != nil { log.Fatalf("error syncing repo: %v", err) } + log.Printf("wait %d seconds", *flWait) + time.Sleep(time.Duration(*flWait) * time.Second) + log.Println("done") } // syncRepo syncs the branch of a given repository to the destination at the given rev. From 19751abe138b7a92927ab56d39ecd55ea9a77ea7 Mon Sep 17 00:00:00 2001 From: Johan Euphrosine Date: Mon, 2 Feb 2015 18:02:18 -0800 Subject: [PATCH 07/50] contrib/git-sync: add demo --- contrib/git-sync/demo/README.md | 28 +++++++++++ contrib/git-sync/demo/blog/archetypes/.keep | 0 contrib/git-sync/demo/blog/config.toml | 3 ++ contrib/git-sync/demo/blog/content/about.md | 9 ++++ .../git-sync/demo/blog/content/post/first.md | 9 ++++ contrib/git-sync/demo/blog/layouts/.keep | 0 contrib/git-sync/demo/blog/static/.keep | 0 contrib/git-sync/demo/config/pod.yaml | 50 +++++++++++++++++++ contrib/git-sync/demo/config/service.yaml | 8 +++ contrib/git-sync/demo/hugo/Dockerfile | 13 +++++ contrib/git-sync/demo/hugo/run-hugo | 6 +++ 11 files changed, 126 insertions(+) create mode 100644 contrib/git-sync/demo/README.md create mode 100644 contrib/git-sync/demo/blog/archetypes/.keep create mode 100644 contrib/git-sync/demo/blog/config.toml create mode 100644 contrib/git-sync/demo/blog/content/about.md create mode 100644 contrib/git-sync/demo/blog/content/post/first.md create mode 100644 contrib/git-sync/demo/blog/layouts/.keep create mode 100644 contrib/git-sync/demo/blog/static/.keep create mode 100644 contrib/git-sync/demo/config/pod.yaml create mode 100644 contrib/git-sync/demo/config/service.yaml create mode 100644 contrib/git-sync/demo/hugo/Dockerfile create mode 100755 contrib/git-sync/demo/hugo/run-hugo diff --git a/contrib/git-sync/demo/README.md b/contrib/git-sync/demo/README.md new file mode 100644 index 00000000000..b458135bfd9 --- /dev/null +++ b/contrib/git-sync/demo/README.md @@ -0,0 +1,28 @@ +# git-blog-demo + +This demo shows how to use the `git-sync` sidekick container along side `volumes` and `volumeMounts` to create a markdown powered blog. + +## How it works + +The pod is composed of 3 containers that share directories using 2 volumes: + +- The `git-sync` container clones a git repo into the `markdown` volume +- The `hugo` container read from the `markdown` volume and render it into the `html` volume. +- The `nginx` container serve the content from the `html` volume. + +## Usage + +Build the demo containers, and push them to a registry +`` +docker build -t /git-sync .. +docker build -t /hugo hugo/ +docker push /hugo /git-sync +``` + +Create the pod and the service for the blog +``` +kubectl pods create config/pod.html +kubectl services create config/pod.html +``` + +Open the external ip in your browser diff --git a/contrib/git-sync/demo/blog/archetypes/.keep b/contrib/git-sync/demo/blog/archetypes/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/contrib/git-sync/demo/blog/config.toml b/contrib/git-sync/demo/blog/config.toml new file mode 100644 index 00000000000..ab3aa9e446e --- /dev/null +++ b/contrib/git-sync/demo/blog/config.toml @@ -0,0 +1,3 @@ +baseurl = "http://example.com" +languageCode = "en-us" +title = "example blog" diff --git a/contrib/git-sync/demo/blog/content/about.md b/contrib/git-sync/demo/blog/content/about.md new file mode 100644 index 00000000000..afb2ac0f4bb --- /dev/null +++ b/contrib/git-sync/demo/blog/content/about.md @@ -0,0 +1,9 @@ ++++ +date = "2014-12-19T15:29:48-08:00" +draft = true +title = "about" ++++ + +## A headline + +Some content about the blog. diff --git a/contrib/git-sync/demo/blog/content/post/first.md b/contrib/git-sync/demo/blog/content/post/first.md new file mode 100644 index 00000000000..630bbbd01d3 --- /dev/null +++ b/contrib/git-sync/demo/blog/content/post/first.md @@ -0,0 +1,9 @@ ++++ +date = "2014-12-19T15:30:18-08:00" +draft = true +title = "first" ++++ + +## first port + +This is the first post. diff --git a/contrib/git-sync/demo/blog/layouts/.keep b/contrib/git-sync/demo/blog/layouts/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/contrib/git-sync/demo/blog/static/.keep b/contrib/git-sync/demo/blog/static/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/contrib/git-sync/demo/config/pod.yaml b/contrib/git-sync/demo/config/pod.yaml new file mode 100644 index 00000000000..75ff074c18b --- /dev/null +++ b/contrib/git-sync/demo/config/pod.yaml @@ -0,0 +1,50 @@ +id: blog-pod +kind: Pod +apiVersion: v1beta1 +desiredState: + manifest: + version: v1beta1 + containers: + - name: git-sync + image: proppy/git-sync + imagePullPolicy: PullAlways + env:: + - name: GIT_SYNC_REPO + value: https://github.com/proppy/blog.git + - name: GIT_SYNC_DEST + value: /git + volumeMounts: + - name: markdown + mountPath: /git + - name: hugo + image: proppy/hugo + imagePullPolicy: PullAlways + env: + - name: SRC + value: /src + - name: BUILD_DRAFT + value: 'true' + - name: BASE_URL + value: kube.proppy.sh + volumeMounts: + - name: markdown + mountPath: /src + - name: html + mountPath: /dest + - name: nginx + image: nginx + volumeMounts: + - name: html + mountPath: /usr/share/nginx/html + ports: + - name: http-server + containerPort: 80 + volumes: + - name: markdown + source: + emptyDir: {} + - name: html + source: + emptyDir: {} +labels: + name: blog diff --git a/contrib/git-sync/demo/config/service.yaml b/contrib/git-sync/demo/config/service.yaml new file mode 100644 index 00000000000..646b4c59aa6 --- /dev/null +++ b/contrib/git-sync/demo/config/service.yaml @@ -0,0 +1,8 @@ +id: blog-service +kind: Service +apiVersion: v1beta1 +port: 80 +containerPort: http-server +selector: + name: blog +createExternalLoadBalancer: true diff --git a/contrib/git-sync/demo/hugo/Dockerfile b/contrib/git-sync/demo/hugo/Dockerfile new file mode 100644 index 00000000000..40af5e67ed8 --- /dev/null +++ b/contrib/git-sync/demo/hugo/Dockerfile @@ -0,0 +1,13 @@ +FROM golang +RUN go get -v github.com/spf13/hugo +RUN git clone --recursive https://github.com/spf13/hugoThemes.git /themes +VOLUME ["/src", "/dest"] +EXPOSE 1313 +ENV SRC /src +ENV DEST /dest +ENV THEME hyde +ENV BUILD_DRAFT false +ENV BASE_URL "" +ADD run-hugo /run-hugo +ENTRYPOINT ["/run-hugo"] +CMD ["server", "--source=${SRC}", "--theme=${THEME}", "--buildDrafts=${BUILD_DRAFT}", "--baseUrl=${BASE_URL}", "--watch", "--destination=${DEST}", "--appendPort=false"] diff --git a/contrib/git-sync/demo/hugo/run-hugo b/contrib/git-sync/demo/hugo/run-hugo new file mode 100755 index 00000000000..c90daa85e25 --- /dev/null +++ b/contrib/git-sync/demo/hugo/run-hugo @@ -0,0 +1,6 @@ +#!/bin/bash +set -ex +if [ ! -d ${SRC}/themes ]; then + ln -s /themes ${SRC}/themes +fi +hugo $(eval echo $*) # force default CMD env expansion From 88734263141b50309f1ec0c4a6122a5c544a299a Mon Sep 17 00:00:00 2001 From: Johan Euphrosine Date: Mon, 2 Feb 2015 18:05:26 -0800 Subject: [PATCH 08/50] contrib/git-sync/demo: fix README, add license header --- contrib/git-sync/demo/README.md | 6 ++++-- contrib/git-sync/demo/hugo/run-hugo | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/contrib/git-sync/demo/README.md b/contrib/git-sync/demo/README.md index b458135bfd9..e2a72465b56 100644 --- a/contrib/git-sync/demo/README.md +++ b/contrib/git-sync/demo/README.md @@ -13,16 +13,18 @@ The pod is composed of 3 containers that share directories using 2 volumes: ## Usage Build the demo containers, and push them to a registry -`` + +``` docker build -t /git-sync .. docker build -t /hugo hugo/ docker push /hugo /git-sync ``` Create the pod and the service for the blog + ``` kubectl pods create config/pod.html kubectl services create config/pod.html ``` -Open the external ip in your browser +Open the service external ip in your browser diff --git a/contrib/git-sync/demo/hugo/run-hugo b/contrib/git-sync/demo/hugo/run-hugo index c90daa85e25..37636025523 100755 --- a/contrib/git-sync/demo/hugo/run-hugo +++ b/contrib/git-sync/demo/hugo/run-hugo @@ -1,4 +1,19 @@ #!/bin/bash + +# Copyright 2014 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + set -ex if [ ! -d ${SRC}/themes ]; then ln -s /themes ${SRC}/themes From 874859f6d3378a5cd065b54646ef643b1d4ca211 Mon Sep 17 00:00:00 2001 From: Brendan Burns Date: Tue, 3 Feb 2015 22:15:12 -0800 Subject: [PATCH 09/50] Make the replication controller more resilient to event expiration in watch. --- pkg/controller/replication_controller.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pkg/controller/replication_controller.go b/pkg/controller/replication_controller.go index 2f9c03221b5..6151edf68eb 100644 --- a/pkg/controller/replication_controller.go +++ b/pkg/controller/replication_controller.go @@ -141,11 +141,24 @@ func (rm *ReplicationManager) watchControllers(resourceVersion *string) { } if event.Type == watch.Error { util.HandleError(fmt.Errorf("error from watch during sync: %v", errors.FromObject(event.Object))) + // Clear the resource version, this may cause us to skip some elements on the watch, + // but we'll catch them on the synchronize() call, so it works out. + *resourceVersion = "" continue } glog.V(4).Infof("Got watch: %#v", event) rc, ok := event.Object.(*api.ReplicationController) if !ok { + if status, ok := event.Object.(*api.Status); ok { + if status.Status == api.StatusFailure { + glog.Errorf("failed to watch: %v", status) + // Clear resource version here, as above, this won't hurt consistency, but we + // should consider introspecting more carefully here. (or make the apiserver smarter) + // "why not both?" + *resourceVersion = "" + continue + } + } util.HandleError(fmt.Errorf("unexpected object: %#v", event.Object)) continue } From e2baf049c0e84a8b0dcea9e097788652824335bc Mon Sep 17 00:00:00 2001 From: Jimmi Dyson Date: Wed, 4 Feb 2015 09:31:39 +0000 Subject: [PATCH 10/50] kubectl proxy: make static prefix configurable --- docs/kubectl.md | 3 ++- pkg/kubectl/cmd/proxy.go | 10 ++++++++-- pkg/kubectl/proxy_server.go | 4 ++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/docs/kubectl.md b/docs/kubectl.md index 902a5a33858..9538279820f 100644 --- a/docs/kubectl.md +++ b/docs/kubectl.md @@ -75,7 +75,8 @@ Usage: --v=0: log level for V logs --validate=false: If true, use a schema to validate the input before sending it --vmodule=: comma-separated list of pattern=N settings for file-filtered logging - -w, --www="": Also serve static files from the given directory under the prefix /static + -w, --www="": Also serve static files from the given directory under the specified prefix + -P, --www-prefix="/static/": Prefix to serve static files under, if static file dir is specified ``` diff --git a/pkg/kubectl/cmd/proxy.go b/pkg/kubectl/cmd/proxy.go index 920562a62ea..508c01b5a72 100644 --- a/pkg/kubectl/cmd/proxy.go +++ b/pkg/kubectl/cmd/proxy.go @@ -18,6 +18,7 @@ package cmd import ( "io" + "strings" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" "github.com/golang/glog" @@ -36,12 +37,17 @@ func (f *Factory) NewCmdProxy(out io.Writer) *cobra.Command { clientConfig, err := f.ClientConfig(cmd) checkErr(err) - server, err := kubectl.NewProxyServer(GetFlagString(cmd, "www"), clientConfig) + staticPrefix := GetFlagString(cmd, "www-prefix") + if !strings.HasSuffix(staticPrefix, "/") { + staticPrefix += "/" + } + server, err := kubectl.NewProxyServer(GetFlagString(cmd, "www"), staticPrefix, clientConfig) checkErr(err) glog.Fatal(server.Serve(port)) }, } - cmd.Flags().StringP("www", "w", "", "Also serve static files from the given directory under the prefix /static") + cmd.Flags().StringP("www", "w", "", "Also serve static files from the given directory under the specified prefix") + cmd.Flags().StringP("www-prefix", "P", "/static/", "Prefix to serve static files under, if static file dir is specified") cmd.Flags().IntP("port", "p", 8001, "The port on which to run the proxy") return cmd } diff --git a/pkg/kubectl/proxy_server.go b/pkg/kubectl/proxy_server.go index a92e0485c4f..29498a80d83 100644 --- a/pkg/kubectl/proxy_server.go +++ b/pkg/kubectl/proxy_server.go @@ -33,7 +33,7 @@ type ProxyServer struct { // NewProxyServer creates and installs a new ProxyServer. // It automatically registers the created ProxyServer to http.DefaultServeMux. -func NewProxyServer(filebase string, cfg *client.Config) (*ProxyServer, error) { +func NewProxyServer(filebase string, staticPrefix string, cfg *client.Config) (*ProxyServer, error) { prefix := cfg.Prefix if prefix == "" { prefix = "/api" @@ -47,7 +47,7 @@ func NewProxyServer(filebase string, cfg *client.Config) (*ProxyServer, error) { return nil, err } http.Handle("/api/", http.StripPrefix("/api/", proxy)) - http.Handle("/static/", newFileHandler("/static/", filebase)) + http.Handle(staticPrefix, newFileHandler(staticPrefix, filebase)) return proxy, nil } From 78f66a6ce9680114cef683196d7745a765d82bd1 Mon Sep 17 00:00:00 2001 From: Wojciech Tyczynski Date: Wed, 4 Feb 2015 18:14:17 +0100 Subject: [PATCH 11/50] Check Docker version in Kubelet /healthz handler --- pkg/kubelet/kubelet.go | 17 +++++++++++++++++ pkg/kubelet/server.go | 23 +++++++++++++++++++++-- pkg/kubelet/server_test.go | 5 +++++ 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/pkg/kubelet/kubelet.go b/pkg/kubelet/kubelet.go index bd3cd98f46b..fe5cd4890ad 100644 --- a/pkg/kubelet/kubelet.go +++ b/pkg/kubelet/kubelet.go @@ -1361,6 +1361,23 @@ func (kl *Kubelet) syncLoop(updates <-chan PodUpdate, handler SyncHandler) { } } +// Returns Docker version for this Kubelet. +func (kl *Kubelet) GetDockerVersion() (string, error) { + if kl.dockerClient == nil { + return "", fmt.Errorf("No Docker client") + } + env, err := kl.dockerClient.Version() + if err != nil { + return "", err + } + for _, entry := range *env { + if strings.HasPrefix(entry, "Version=") { + return strings.Split(entry, "=")[1], nil + } + } + return "", fmt.Errorf("Docker version unknown") +} + // GetKubeletContainerLogs returns logs from the container // The second parameter of GetPodStatus and FindPodContainer methods represents pod UUID, which is allowed to be blank // TODO: this method is returning logs of random container attempts, when it should be returning the most recent attempt diff --git a/pkg/kubelet/server.go b/pkg/kubelet/server.go index 222cedb52e2..fdcb402072e 100644 --- a/pkg/kubelet/server.go +++ b/pkg/kubelet/server.go @@ -31,7 +31,6 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" - "github.com/GoogleCloudPlatform/kubernetes/pkg/healthz" "github.com/GoogleCloudPlatform/kubernetes/pkg/httplog" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/types" @@ -64,6 +63,7 @@ func ListenAndServeKubeletServer(host HostInterface, address net.IP, port uint, type HostInterface interface { GetContainerInfo(podFullName string, uid types.UID, containerName string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error) GetRootInfo(req *info.ContainerInfoRequest) (*info.ContainerInfo, error) + GetDockerVersion() (string, error) GetMachineInfo() (*info.MachineInfo, error) GetBoundPods() ([]api.BoundPod, error) GetPodByName(namespace, name string) (*api.BoundPod, bool) @@ -88,7 +88,7 @@ func NewServer(host HostInterface, enableDebuggingHandlers bool) Server { // InstallDefaultHandlers registers the default set of supported HTTP request patterns with the mux. func (s *Server) InstallDefaultHandlers() { - healthz.InstallHandler(s.mux) + s.mux.HandleFunc("/healthz", s.handleHealthz) s.mux.HandleFunc("/podInfo", s.handlePodInfoOld) s.mux.HandleFunc("/api/v1beta1/podInfo", s.handlePodInfoVersioned) s.mux.HandleFunc("/boundPods", s.handleBoundPods) @@ -109,6 +109,25 @@ func (s *Server) error(w http.ResponseWriter, err error) { http.Error(w, fmt.Sprintf("Internal Error: %v", err), http.StatusInternalServerError) } +// handleHealthz handles /healthz request and checks Docker version +func (s *Server) handleHealthz(w http.ResponseWriter, req *http.Request) { + version, err := s.host.GetDockerVersion() + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte("unknown Docker version")) + return + } + const minDockerVersion = "1.3.0" + if version < minDockerVersion { + w.WriteHeader(http.StatusInternalServerError) + msg := "Docker version is too old (" + version + ")" + w.Write([]byte(msg)) + return; + } + w.WriteHeader(http.StatusOK) + w.Write([]byte("ok")) +} + // handleContainerLogs handles containerLogs request against the Kubelet func (s *Server) handleContainerLogs(w http.ResponseWriter, req *http.Request) { defer req.Body.Close() diff --git a/pkg/kubelet/server_test.go b/pkg/kubelet/server_test.go index 69dd77eeb12..2dd2f45e46c 100644 --- a/pkg/kubelet/server_test.go +++ b/pkg/kubelet/server_test.go @@ -42,6 +42,7 @@ type fakeKubelet struct { boundPodsFunc func() ([]api.BoundPod, error) logFunc func(w http.ResponseWriter, req *http.Request) runFunc func(podFullName string, uid types.UID, containerName string, cmd []string) ([]byte, error) + dockerVersionFunc func() (string, error) containerLogsFunc func(podFullName, containerName, tail string, follow bool, stdout, stderr io.Writer) error } @@ -61,6 +62,10 @@ func (fk *fakeKubelet) GetRootInfo(req *info.ContainerInfoRequest) (*info.Contai return fk.rootInfoFunc(req) } +func (fk *fakeKubelet) GetDockerVersion() (string, error) { + return fk.dockerVersionFunc() +} + func (fk *fakeKubelet) GetMachineInfo() (*info.MachineInfo, error) { return fk.machineInfoFunc() } From ead67108ce911a3b4a7e38274ca645603ec4772f Mon Sep 17 00:00:00 2001 From: Wojciech Tyczynski Date: Wed, 4 Feb 2015 20:50:21 +0100 Subject: [PATCH 12/50] Use existing method to get Docker version. --- pkg/kubelet/dockertools/docker.go | 5 +++-- pkg/kubelet/dockertools/docker_test.go | 2 +- pkg/kubelet/kubelet.go | 16 ++++------------ pkg/kubelet/kubelet_test.go | 4 ++++ pkg/kubelet/server.go | 25 +++++++++++++++++++++---- pkg/kubelet/server_test.go | 4 ++-- 6 files changed, 35 insertions(+), 21 deletions(-) diff --git a/pkg/kubelet/dockertools/docker.go b/pkg/kubelet/dockertools/docker.go index bb030743f77..04115703c9d 100644 --- a/pkg/kubelet/dockertools/docker.go +++ b/pkg/kubelet/dockertools/docker.go @@ -104,7 +104,7 @@ type dockerContainerCommandRunner struct { var dockerAPIVersionWithExec = []uint{1, 15} // Returns the major and minor version numbers of docker server. -func (d *dockerContainerCommandRunner) getDockerServerVersion() ([]uint, error) { +func (d *dockerContainerCommandRunner) GetDockerServerVersion() ([]uint, error) { env, err := d.client.Version() if err != nil { return nil, fmt.Errorf("failed to get docker server version - %v", err) @@ -127,7 +127,7 @@ func (d *dockerContainerCommandRunner) getDockerServerVersion() ([]uint, error) } func (d *dockerContainerCommandRunner) nativeExecSupportExists() (bool, error) { - version, err := d.getDockerServerVersion() + version, err := d.GetDockerServerVersion() if err != nil { return false, err } @@ -626,4 +626,5 @@ func parseImageName(image string) (string, string) { type ContainerCommandRunner interface { RunInContainer(containerID string, cmd []string) ([]byte, error) + GetDockerServerVersion() ([]uint, error) } diff --git a/pkg/kubelet/dockertools/docker_test.go b/pkg/kubelet/dockertools/docker_test.go index 8609036ae1b..5108fbc4b14 100644 --- a/pkg/kubelet/dockertools/docker_test.go +++ b/pkg/kubelet/dockertools/docker_test.go @@ -123,7 +123,7 @@ func TestContainerManifestNaming(t *testing.T) { func TestGetDockerServerVersion(t *testing.T) { fakeDocker := &FakeDockerClient{VersionInfo: docker.Env{"Client version=1.2", "Server version=1.1.3", "Server API version=1.15"}} runner := dockerContainerCommandRunner{fakeDocker} - version, err := runner.getDockerServerVersion() + version, err := runner.GetDockerServerVersion() if err != nil { t.Errorf("got error while getting docker server version - %s", err) } diff --git a/pkg/kubelet/kubelet.go b/pkg/kubelet/kubelet.go index fe5cd4890ad..f24f9383f3f 100644 --- a/pkg/kubelet/kubelet.go +++ b/pkg/kubelet/kubelet.go @@ -1362,20 +1362,12 @@ func (kl *Kubelet) syncLoop(updates <-chan PodUpdate, handler SyncHandler) { } // Returns Docker version for this Kubelet. -func (kl *Kubelet) GetDockerVersion() (string, error) { +func (kl *Kubelet) GetDockerVersion() ([]uint, error) { if kl.dockerClient == nil { - return "", fmt.Errorf("No Docker client") + return nil, fmt.Errorf("no Docker client") } - env, err := kl.dockerClient.Version() - if err != nil { - return "", err - } - for _, entry := range *env { - if strings.HasPrefix(entry, "Version=") { - return strings.Split(entry, "=")[1], nil - } - } - return "", fmt.Errorf("Docker version unknown") + dockerRunner := dockertools.NewDockerContainerCommandRunner(kl.dockerClient) + return dockerRunner.GetDockerServerVersion() } // GetKubeletContainerLogs returns logs from the container diff --git a/pkg/kubelet/kubelet_test.go b/pkg/kubelet/kubelet_test.go index b3b44cb4c49..c7130b15bde 100644 --- a/pkg/kubelet/kubelet_test.go +++ b/pkg/kubelet/kubelet_test.go @@ -1367,6 +1367,10 @@ func (f *fakeContainerCommandRunner) RunInContainer(id string, cmd []string) ([] return []byte{}, f.E } +func (f *fakeContainerCommandRunner) GetDockerServerVersion() ([]uint, error) { + return nil, nil +} + func TestRunInContainerNoSuchPod(t *testing.T) { fakeCommandRunner := fakeContainerCommandRunner{} kubelet, fakeDocker := newTestKubelet(t) diff --git a/pkg/kubelet/server.go b/pkg/kubelet/server.go index fdcb402072e..6ac65dc280f 100644 --- a/pkg/kubelet/server.go +++ b/pkg/kubelet/server.go @@ -63,7 +63,7 @@ func ListenAndServeKubeletServer(host HostInterface, address net.IP, port uint, type HostInterface interface { GetContainerInfo(podFullName string, uid types.UID, containerName string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error) GetRootInfo(req *info.ContainerInfoRequest) (*info.ContainerInfo, error) - GetDockerVersion() (string, error) + GetDockerVersion() ([]uint, error) GetMachineInfo() (*info.MachineInfo, error) GetBoundPods() ([]api.BoundPod, error) GetPodByName(namespace, name string) (*api.BoundPod, bool) @@ -109,16 +109,33 @@ func (s *Server) error(w http.ResponseWriter, err error) { http.Error(w, fmt.Sprintf("Internal Error: %v", err), http.StatusInternalServerError) } +func isValidDockerVersion(ver []uint) (bool, string) { + minAllowedVersion := []uint{1, 3, 0} + for i := 0; i < len(ver) && i < len(minAllowedVersion); i++ { + if ver[i] != minAllowedVersion[i] { + if ver[i] < minAllowedVersion[i] { + versions := make([]string, len(ver)) + for i, v := range(ver) { + versions[i] = fmt.Sprint(v) + } + return false, strings.Join(versions, ".") + } + return true, "" + } + } + return true, "" +} + // handleHealthz handles /healthz request and checks Docker version func (s *Server) handleHealthz(w http.ResponseWriter, req *http.Request) { - version, err := s.host.GetDockerVersion() + versions, err := s.host.GetDockerVersion() if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("unknown Docker version")) return } - const minDockerVersion = "1.3.0" - if version < minDockerVersion { + valid, version := isValidDockerVersion(versions) + if !valid { w.WriteHeader(http.StatusInternalServerError) msg := "Docker version is too old (" + version + ")" w.Write([]byte(msg)) diff --git a/pkg/kubelet/server_test.go b/pkg/kubelet/server_test.go index 2dd2f45e46c..32c5f9967e9 100644 --- a/pkg/kubelet/server_test.go +++ b/pkg/kubelet/server_test.go @@ -42,7 +42,7 @@ type fakeKubelet struct { boundPodsFunc func() ([]api.BoundPod, error) logFunc func(w http.ResponseWriter, req *http.Request) runFunc func(podFullName string, uid types.UID, containerName string, cmd []string) ([]byte, error) - dockerVersionFunc func() (string, error) + dockerVersionFunc func() ([]uint, error) containerLogsFunc func(podFullName, containerName, tail string, follow bool, stdout, stderr io.Writer) error } @@ -62,7 +62,7 @@ func (fk *fakeKubelet) GetRootInfo(req *info.ContainerInfoRequest) (*info.Contai return fk.rootInfoFunc(req) } -func (fk *fakeKubelet) GetDockerVersion() (string, error) { +func (fk *fakeKubelet) GetDockerVersion() ([]uint, error) { return fk.dockerVersionFunc() } From 3229a634a00c0346eee84384fb298eec46d43403 Mon Sep 17 00:00:00 2001 From: Paul Morie Date: Wed, 4 Feb 2015 14:21:33 -0500 Subject: [PATCH 13/50] Clarify commenting in endpoints_controller --- pkg/registry/service/rest_test.go | 2 +- pkg/service/endpoints_controller.go | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pkg/registry/service/rest_test.go b/pkg/registry/service/rest_test.go index 82469b13f9f..1bb4776f10d 100644 --- a/pkg/registry/service/rest_test.go +++ b/pkg/registry/service/rest_test.go @@ -92,7 +92,7 @@ func TestServiceStorageValidatesCreate(t *testing.T) { Selector: map[string]string{"bar": "baz"}, }, }, - "empty selector": { + "empty port": { ObjectMeta: api.ObjectMeta{Name: "foo"}, Spec: api.ServiceSpec{ Selector: map[string]string{"bar": "baz"}, diff --git a/pkg/service/endpoints_controller.go b/pkg/service/endpoints_controller.go index d205914a3cc..3801972bb22 100644 --- a/pkg/service/endpoints_controller.go +++ b/pkg/service/endpoints_controller.go @@ -30,7 +30,7 @@ import ( "github.com/golang/glog" ) -// EndpointController manages service endpoints. +// EndpointController manages selector-based service endpoints. type EndpointController struct { client *client.Client } @@ -42,7 +42,7 @@ func NewEndpointController(client *client.Client) *EndpointController { } } -// SyncServiceEndpoints syncs service endpoints. +// SyncServiceEndpoints syncs endpoints for services with selectors. func (e *EndpointController) SyncServiceEndpoints() error { services, err := e.client.Services(api.NamespaceAll).List(labels.Everything()) if err != nil { @@ -52,7 +52,8 @@ func (e *EndpointController) SyncServiceEndpoints() error { var resultErr error for _, service := range services.Items { if service.Spec.Selector == nil { - // services without a selector receive no endpoints. The last endpoint will be used. + // services without a selector receive no endpoints from this controller; + // these services will receive the endpoints that are created out-of-band via the REST API. continue } From 6bb374ca5485bf8bcc57cc61d1ad8b178235d541 Mon Sep 17 00:00:00 2001 From: Alex Robinson Date: Wed, 4 Feb 2015 22:21:34 +0000 Subject: [PATCH 14/50] Add a few extra test cases for API validation of labels that would have caught a bug that was in past versions. Also, copy the label key format requirements from the API types.go comments into the labels doc. --- docs/labels.md | 9 ++++++ pkg/api/validation/validation_test.go | 44 +++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/docs/labels.md b/docs/labels.md index 46a9b00ec97..9f9d4ee3629 100644 --- a/docs/labels.md +++ b/docs/labels.md @@ -11,6 +11,15 @@ key/value labels set on it, with at most one label with a particular key. } ``` +While there are no restrictions on the format of label values, label keys must be of the form: +``` +label-key ::= prefixed-name | name +prefixed-name ::= prefix '/' name +prefix ::= DNS_SUBDOMAIN +name ::= DNS_LABEL +``` +DNS_LABEL and DNS_SUBDOMAIN are defined in the [identifiers design doc](/docs/design/identifiers.md). The prefix is optional. If the prefix is not specified, the key is assumed to be private to the user. Other system components that wish to use labels must specify a prefix. The "kubernetes.io/" prefix is reserved for use by kubernetes components. + Unlike [names and UIDs](identifiers.md), labels do not provide uniqueness. In general, we expect many objects to carry the same label(s). Via a _label selector_, the client/user can identify a set of objects. The label selector is the core grouping primitive in Kubernetes. diff --git a/pkg/api/validation/validation_test.go b/pkg/api/validation/validation_test.go index 210f4c72963..72dbcf21c73 100644 --- a/pkg/api/validation/validation_test.go +++ b/pkg/api/validation/validation_test.go @@ -876,6 +876,26 @@ func TestValidatePodUpdate(t *testing.T) { false, "port change", }, + { + api.Pod{ + ObjectMeta: api.ObjectMeta{ + Name: "foo", + Labels: map[string]string{ + "foo": "bar", + }, + }, + }, + api.Pod{ + ObjectMeta: api.ObjectMeta{ + Name: "foo", + Labels: map[string]string{ + "Bar": "foo", + }, + }, + }, + true, + "bad label change", + }, } for _, test := range tests { @@ -1640,6 +1660,17 @@ func TestValidateMinionUpdate(t *testing.T) { Labels: map[string]string{"bar": "fooobaz"}, }, }, true}, + {api.Node{ + ObjectMeta: api.ObjectMeta{ + Name: "foo", + Labels: map[string]string{"foo": "baz"}, + }, + }, api.Node{ + ObjectMeta: api.ObjectMeta{ + Name: "foo", + Labels: map[string]string{"Foo": "baz"}, + }, + }, false}, } for i, test := range tests { errs := ValidateMinionUpdate(&test.oldMinion, &test.minion) @@ -1796,6 +1827,19 @@ func TestValidateServiceUpdate(t *testing.T) { PortalIP: "127.0.0.2", }, }, false}, + { // 10 + api.Service{ + ObjectMeta: api.ObjectMeta{ + Name: "foo", + Labels: map[string]string{"foo": "baz"}, + }, + }, + api.Service{ + ObjectMeta: api.ObjectMeta{ + Name: "foo", + Labels: map[string]string{"Foo": "baz"}, + }, + }, false}, } for i, test := range tests { errs := ValidateServiceUpdate(&test.oldService, &test.service) From ad4c2ee630a4ac780cdba86a16027b541742adb7 Mon Sep 17 00:00:00 2001 From: Jerzy Szczepkowski Date: Wed, 4 Feb 2015 23:41:27 +0100 Subject: [PATCH 15/50] Improve "constraint violation" error message. --- pkg/constraint/constraint.go | 9 +++++++-- pkg/constraint/constraint_test.go | 13 +++++++------ pkg/registry/etcd/etcd.go | 4 ++-- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/pkg/constraint/constraint.go b/pkg/constraint/constraint.go index 8acc8c073d8..da0c3749e14 100644 --- a/pkg/constraint/constraint.go +++ b/pkg/constraint/constraint.go @@ -17,11 +17,16 @@ limitations under the License. package constraint import ( + "fmt" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" ) // Allowed returns true if pods is a collection of bound pods // which can run without conflict on a single minion. -func Allowed(pods []api.BoundPod) bool { - return !PortsConflict(pods) +func Allowed(pods []api.BoundPod) error { + if (PortsConflict(pods)) { + return fmt.Errorf("conflicting ports"); + } + return nil; } diff --git a/pkg/constraint/constraint_test.go b/pkg/constraint/constraint_test.go index 1677d935df5..530428f91cc 100644 --- a/pkg/constraint/constraint_test.go +++ b/pkg/constraint/constraint_test.go @@ -17,6 +17,7 @@ limitations under the License. package constraint import ( + "fmt" "testing" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" @@ -40,11 +41,11 @@ func podWithContainers(containers ...api.Container) api.BoundPod { func TestAllowed(t *testing.T) { table := []struct { - allowed bool + err error pods []api.BoundPod }{ { - allowed: true, + err: nil, pods: []api.BoundPod{ podWithContainers( containerWithHostPorts(1, 2, 3), @@ -57,7 +58,7 @@ func TestAllowed(t *testing.T) { }, }, { - allowed: true, + err: nil, pods: []api.BoundPod{ podWithContainers( containerWithHostPorts(0, 0), @@ -70,7 +71,7 @@ func TestAllowed(t *testing.T) { }, }, { - allowed: false, + err: fmt.Errorf("conflicting ports"), pods: []api.BoundPod{ podWithContainers( containerWithHostPorts(3, 3), @@ -78,7 +79,7 @@ func TestAllowed(t *testing.T) { }, }, { - allowed: false, + err: fmt.Errorf("conflicting ports"), pods: []api.BoundPod{ podWithContainers( containerWithHostPorts(6), @@ -91,7 +92,7 @@ func TestAllowed(t *testing.T) { } for _, item := range table { - if e, a := item.allowed, Allowed(item.pods); e != a { + if e, a := item.err, Allowed(item.pods); e != a && e.Error() != a.Error() { t.Errorf("Expected %v, got %v: \n%v\v", e, a, item.pods) } } diff --git a/pkg/registry/etcd/etcd.go b/pkg/registry/etcd/etcd.go index d2bd576cb52..49c32eff69e 100644 --- a/pkg/registry/etcd/etcd.go +++ b/pkg/registry/etcd/etcd.go @@ -214,8 +214,8 @@ func (r *Registry) assignPod(ctx api.Context, podID string, machine string) erro err = r.AtomicUpdate(contKey, &api.BoundPods{}, func(in runtime.Object) (runtime.Object, error) { boundPodList := in.(*api.BoundPods) boundPodList.Items = append(boundPodList.Items, *boundPod) - if !constraint.Allowed(boundPodList.Items) { - return nil, fmt.Errorf("the assignment would cause a constraint violation") + if e := constraint.Allowed(boundPodList.Items); e != nil { + return nil, fmt.Errorf("the assignment would cause the following constraint violation: %v", e) } return boundPodList, nil }) From 283919e8b138efbd8a3108dec0c91c19728d4f38 Mon Sep 17 00:00:00 2001 From: Yu-Ju Hong Date: Wed, 4 Feb 2015 15:44:46 -0800 Subject: [PATCH 16/50] Fix tests in validation_test Some tests expect the error cases to fail for a specific reason, but they could fail for other reasons and still pass. This caused some changes to break the tests without noticing the breakage. Example are the recent defaulting PR This change fixes such tests and also updates some obsolete tests. --- pkg/api/validation/validation_test.go | 162 ++++++++++++++++++-------- 1 file changed, 114 insertions(+), 48 deletions(-) diff --git a/pkg/api/validation/validation_test.go b/pkg/api/validation/validation_test.go index 92ded41f7ad..60e7107eb18 100644 --- a/pkg/api/validation/validation_test.go +++ b/pkg/api/validation/validation_test.go @@ -214,7 +214,7 @@ func TestValidatePorts(t *testing.T) { "invalid container port": {[]api.Port{{ContainerPort: 65536, Protocol: "TCP"}}, errors.ValidationErrorTypeInvalid, "[0].containerPort"}, "invalid host port": {[]api.Port{{ContainerPort: 80, HostPort: 65536, Protocol: "TCP"}}, errors.ValidationErrorTypeInvalid, "[0].hostPort"}, "invalid protocol": {[]api.Port{{ContainerPort: 80, Protocol: "ICMP"}}, errors.ValidationErrorTypeNotSupported, "[0].protocol"}, - "protocol required": {[]api.Port{{Name: "abc", ContainerPort: 80}}, errors.ValidationErrorTypeRequired, "[0].protocol"}, //yjhong + "protocol required": {[]api.Port{{Name: "abc", ContainerPort: 80}}, errors.ValidationErrorTypeRequired, "[0].protocol"}, } for k, v := range errorCases { errs := validatePorts(v.P) @@ -371,23 +371,26 @@ func TestValidateContainers(t *testing.T) { AllowPrivileged: false, }) errorCases := map[string][]api.Container{ - "zero-length name": {{Name: "", Image: "image"}}, - "name > 63 characters": {{Name: strings.Repeat("a", 64), Image: "image"}}, - "name not a DNS label": {{Name: "a.b.c", Image: "image"}}, + "zero-length name": {{Name: "", Image: "image", ImagePullPolicy: "IfNotPresent"}}, + "name > 63 characters": {{Name: strings.Repeat("a", 64), Image: "image", ImagePullPolicy: "IfNotPresent"}}, + "name not a DNS label": {{Name: "a.b.c", Image: "image", ImagePullPolicy: "IfNotPresent"}}, "name not unique": { - {Name: "abc", Image: "image"}, - {Name: "abc", Image: "image"}, + {Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}, + {Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}, }, - "zero-length image": {{Name: "abc", Image: ""}}, + "zero-length image": {{Name: "abc", Image: "", ImagePullPolicy: "IfNotPresent"}}, "host port not unique": { - {Name: "abc", Image: "image", Ports: []api.Port{{ContainerPort: 80, HostPort: 80}}}, - {Name: "def", Image: "image", Ports: []api.Port{{ContainerPort: 81, HostPort: 80}}}, + {Name: "abc", Image: "image", Ports: []api.Port{{ContainerPort: 80, HostPort: 80, Protocol: "TCP"}}, + ImagePullPolicy: "IfNotPresent"}, + {Name: "def", Image: "image", Ports: []api.Port{{ContainerPort: 81, HostPort: 80, Protocol: "TCP"}}, + ImagePullPolicy: "IfNotPresent"}, }, "invalid env var name": { - {Name: "abc", Image: "image", Env: []api.EnvVar{{Name: "ev.1"}}}, + {Name: "abc", Image: "image", Env: []api.EnvVar{{Name: "ev.1"}}, ImagePullPolicy: "IfNotPresent"}, }, "unknown volume name": { - {Name: "abc", Image: "image", VolumeMounts: []api.VolumeMount{{Name: "anything", MountPath: "/foo"}}}, + {Name: "abc", Image: "image", VolumeMounts: []api.VolumeMount{{Name: "anything", MountPath: "/foo"}}, + ImagePullPolicy: "IfNotPresent"}, }, "invalid lifecycle, no exec command.": { { @@ -398,6 +401,7 @@ func TestValidateContainers(t *testing.T) { Exec: &api.ExecAction{}, }, }, + ImagePullPolicy: "IfNotPresent", }, }, "invalid lifecycle, no http path.": { @@ -409,6 +413,7 @@ func TestValidateContainers(t *testing.T) { HTTPGet: &api.HTTPGetAction{}, }, }, + ImagePullPolicy: "IfNotPresent", }, }, "invalid lifecycle, no action.": { @@ -418,6 +423,7 @@ func TestValidateContainers(t *testing.T) { Lifecycle: &api.Lifecycle{ PreStop: &api.Handler{}, }, + ImagePullPolicy: "IfNotPresent", }, }, "privilege disabled": { @@ -432,6 +438,7 @@ func TestValidateContainers(t *testing.T) { "disk": resource.MustParse("10G"), }, }, + ImagePullPolicy: "IfNotPresent", }, }, "Resource CPU invalid": { @@ -441,6 +448,7 @@ func TestValidateContainers(t *testing.T) { Resources: api.ResourceRequirementSpec{ Limits: getResourceLimits("-10", "0"), }, + ImagePullPolicy: "IfNotPresent", }, }, "Resource Memory invalid": { @@ -450,6 +458,7 @@ func TestValidateContainers(t *testing.T) { Resources: api.ResourceRequirementSpec{ Limits: getResourceLimits("0", "-10"), }, + ImagePullPolicy: "IfNotPresent", }, }, } @@ -553,17 +562,26 @@ func TestValidateManifest(t *testing.T) { } errorCases := map[string]api.ContainerManifest{ - "empty version": {Version: "", ID: "abc"}, - "invalid version": {Version: "bogus", ID: "abc"}, + "empty version": {Version: "", ID: "abc", + RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}}, + DNSPolicy: api.DNSClusterFirst}, + "invalid version": {Version: "bogus", ID: "abc", + RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}}, + DNSPolicy: api.DNSClusterFirst}, "invalid volume name": { - Version: "v1beta1", - ID: "abc", - Volumes: []api.Volume{{Name: "vol.1"}}, + Version: "v1beta1", + ID: "abc", + Volumes: []api.Volume{{Name: "vol.1", Source: api.VolumeSource{EmptyDir: &api.EmptyDir{}}}}, + RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}}, + DNSPolicy: api.DNSClusterFirst, }, "invalid container name": { - Version: "v1beta1", - ID: "abc", - Containers: []api.Container{{Name: "ctr.1", Image: "image"}}, + Version: "v1beta1", + ID: "abc", + Containers: []api.Container{{Name: "ctr.1", Image: "image", ImagePullPolicy: "IfNotPresent", + TerminationMessagePath: "/foo/bar"}}, + RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}}, + DNSPolicy: api.DNSClusterFirst, }, } for k, v := range errorCases { @@ -602,13 +620,22 @@ func TestValidatePodSpec(t *testing.T) { failureCases := map[string]api.PodSpec{ "bad volume": { - Volumes: []api.Volume{{}}, + Volumes: []api.Volume{{}}, + RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}}, + DNSPolicy: api.DNSClusterFirst, }, "bad container": { - Containers: []api.Container{{}}, + Containers: []api.Container{{}}, + RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}}, + DNSPolicy: api.DNSClusterFirst, }, "bad DNS policy": { - DNSPolicy: api.DNSPolicy("invalid"), + DNSPolicy: api.DNSPolicy("invalid"), + RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}}, + }, + "bad restart policy": { + RestartPolicy: api.RestartPolicy{}, + DNSPolicy: api.DNSClusterFirst, }, } for k, v := range failureCases { @@ -652,8 +679,20 @@ func TestValidatePod(t *testing.T) { } errorCases := map[string]api.Pod{ - "bad name": {ObjectMeta: api.ObjectMeta{Name: "", Namespace: "ns"}}, - "bad namespace": {ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: ""}}, + "bad name": { + ObjectMeta: api.ObjectMeta{Name: "", Namespace: "ns"}, + Spec: api.PodSpec{ + RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}}, + DNSPolicy: api.DNSClusterFirst, + }, + }, + "bad namespace": { + ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: ""}, + Spec: api.PodSpec{ + RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}}, + DNSPolicy: api.DNSClusterFirst, + }, + }, "bad spec": { ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "ns"}, Spec: api.PodSpec{ @@ -668,6 +707,10 @@ func TestValidatePod(t *testing.T) { "NoUppercaseOrSpecialCharsLike=Equals": "bar", }, }, + Spec: api.PodSpec{ + RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}}, + DNSPolicy: api.DNSClusterFirst, + }, }, "bad annotation": { ObjectMeta: api.ObjectMeta{ @@ -677,6 +720,10 @@ func TestValidatePod(t *testing.T) { "NoUppercaseOrSpecialCharsLike=Equals": "bar", }, }, + Spec: api.PodSpec{ + RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}}, + DNSPolicy: api.DNSClusterFirst, + }, }, } for k, v := range errorCases { @@ -914,18 +961,53 @@ func TestValidateBoundPods(t *testing.T) { } errorCases := map[string]api.Pod{ - "bad name": {ObjectMeta: api.ObjectMeta{Name: "", Namespace: "ns"}}, - "bad namespace": {ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: ""}}, + "zero-length name": { + ObjectMeta: api.ObjectMeta{Name: "", Namespace: "ns"}, + Spec: api.PodSpec{ + RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}}, + DNSPolicy: api.DNSClusterFirst, + }, + }, + "bad namespace": { + ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: ""}, + Spec: api.PodSpec{ + RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}}, + DNSPolicy: api.DNSClusterFirst, + }, + }, "bad spec": { ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "ns"}, Spec: api.PodSpec{ - Containers: []api.Container{{}}, + Containers: []api.Container{{Name: "name", ImagePullPolicy: "IfNotPresent"}}, + RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}}, + DNSPolicy: api.DNSClusterFirst, + }, + }, + "name > 253 characters": { + ObjectMeta: api.ObjectMeta{Name: strings.Repeat("a", 254), Namespace: "ns"}, + Spec: api.PodSpec{ + RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}}, + DNSPolicy: api.DNSClusterFirst, + }, + }, + "name not a DNS subdomain": { + ObjectMeta: api.ObjectMeta{Name: "a..b.c", Namespace: "ns"}, + Spec: api.PodSpec{ + RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}}, + DNSPolicy: api.DNSClusterFirst, + }, + }, + "name with underscore": { + ObjectMeta: api.ObjectMeta{Name: "a_b_c", Namespace: "ns"}, + Spec: api.PodSpec{ + RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}}, + DNSPolicy: api.DNSClusterFirst, }, }, } for k, v := range errorCases { - if errs := ValidatePod(&v); len(errs) == 0 { - t.Errorf("expected failure for %s", k) + if errs := ValidatePod(&v); len(errs) != 1 { + t.Errorf("expected one failure for %s; got %d: %s", k, len(errs), errs) } } } @@ -1314,7 +1396,9 @@ func TestValidateReplicationController(t *testing.T) { invalidVolumePodTemplate := api.PodTemplate{ Spec: api.PodTemplateSpec{ Spec: api.PodSpec{ - Volumes: []api.Volume{{Name: "gcepd", Source: api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDisk{"my-PD", "ext4", 1, false}}}}, + Volumes: []api.Volume{{Name: "gcepd", Source: api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDisk{"my-PD", "ext4", 1, false}}}}, + RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}}, + DNSPolicy: api.DNSClusterFirst, }, }, } @@ -1502,24 +1586,6 @@ func TestValidateReplicationController(t *testing.T) { } } -func TestValidateBoundPodNoName(t *testing.T) { - errorCases := map[string]api.BoundPod{ - // manifest is tested in api/validation_test.go, ensure it is invoked - "empty version": {ObjectMeta: api.ObjectMeta{Name: "test"}, Spec: api.PodSpec{Containers: []api.Container{{Name: ""}}}}, - - // Name - "zero-length name": {ObjectMeta: api.ObjectMeta{Name: ""}}, - "name > 255 characters": {ObjectMeta: api.ObjectMeta{Name: strings.Repeat("a", 256)}}, - "name not a DNS subdomain": {ObjectMeta: api.ObjectMeta{Name: "a.b.c."}}, - "name with underscore": {ObjectMeta: api.ObjectMeta{Name: "a_b_c"}}, - } - for k, v := range errorCases { - if errs := ValidateBoundPod(&v); len(errs) == 0 { - t.Errorf("expected failure for %s", k) - } - } -} - func TestValidateMinion(t *testing.T) { validSelector := map[string]string{"a": "b"} invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"} From 0010e0214854daf301ac2750832e4b14233fee5c Mon Sep 17 00:00:00 2001 From: Jerzy Szczepkowski Date: Thu, 5 Feb 2015 00:52:34 +0100 Subject: [PATCH 17/50] Fixed formatting. --- pkg/constraint/constraint.go | 6 +++--- pkg/constraint/constraint_test.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/constraint/constraint.go b/pkg/constraint/constraint.go index da0c3749e14..078ca6d59d1 100644 --- a/pkg/constraint/constraint.go +++ b/pkg/constraint/constraint.go @@ -25,8 +25,8 @@ import ( // Allowed returns true if pods is a collection of bound pods // which can run without conflict on a single minion. func Allowed(pods []api.BoundPod) error { - if (PortsConflict(pods)) { - return fmt.Errorf("conflicting ports"); + if PortsConflict(pods) { + return fmt.Errorf("conflicting ports") } - return nil; + return nil } diff --git a/pkg/constraint/constraint_test.go b/pkg/constraint/constraint_test.go index 530428f91cc..bb6f84fc89d 100644 --- a/pkg/constraint/constraint_test.go +++ b/pkg/constraint/constraint_test.go @@ -41,8 +41,8 @@ func podWithContainers(containers ...api.Container) api.BoundPod { func TestAllowed(t *testing.T) { table := []struct { - err error - pods []api.BoundPod + err error + pods []api.BoundPod }{ { err: nil, From 524cdba1011b346df989459265e8b4105ccda156 Mon Sep 17 00:00:00 2001 From: Wojciech Tyczynski Date: Thu, 5 Feb 2015 01:58:26 +0100 Subject: [PATCH 18/50] Fix hack/test-cmd.sh test. --- cmd/kubernetes/kubernetes.go | 3 ++- hack/test-cmd.sh | 2 ++ pkg/kubelet/dockertools/docker.go | 29 +++++++++++++++++++++++++++++ pkg/kubelet/server.go | 4 ++-- pkg/kubelet/server/server.go | 2 +- pkg/util/node.go | 25 ------------------------- 6 files changed, 36 insertions(+), 29 deletions(-) diff --git a/cmd/kubernetes/kubernetes.go b/cmd/kubernetes/kubernetes.go index 9eb08284f56..e01fabd565e 100644 --- a/cmd/kubernetes/kubernetes.go +++ b/cmd/kubernetes/kubernetes.go @@ -33,6 +33,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/client" nodeControllerPkg "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/controller" "github.com/GoogleCloudPlatform/kubernetes/pkg/controller" + "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools" kubeletServer "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/server" "github.com/GoogleCloudPlatform/kubernetes/pkg/master" "github.com/GoogleCloudPlatform/kubernetes/pkg/master/ports" @@ -139,7 +140,7 @@ func startComponents(etcdClient tools.EtcdClient, cl *client.Client, addr net.IP runScheduler(cl) runControllerManager(machineList, cl, *nodeMilliCPU, *nodeMemory) - dockerClient := util.ConnectToDockerOrDie(*dockerEndpoint) + dockerClient := dockertools.ConnectToDockerOrDie(*dockerEndpoint) kubeletServer.SimpleRunKubelet(cl, nil, dockerClient, machineList[0], "/tmp/kubernetes", "", "127.0.0.1", 10250, *masterServiceNamespace, kubeletServer.ProbeVolumePlugins()) } diff --git a/hack/test-cmd.sh b/hack/test-cmd.sh index 5d48dcaa10b..2fd73970294 100755 --- a/hack/test-cmd.sh +++ b/hack/test-cmd.sh @@ -55,6 +55,7 @@ kube::log::status "Starting kubelet in masterless mode" "${KUBE_OUTPUT_HOSTBIN}/kubelet" \ --really_crash_for_testing=true \ --root_dir=/tmp/kubelet.$$ \ + --docker_endpoint="fake://" \ --address="127.0.0.1" \ --port="$KUBELET_PORT" 1>&2 & KUBELET_PID=$! @@ -65,6 +66,7 @@ kube::log::status "Starting kubelet in masterful mode" "${KUBE_OUTPUT_HOSTBIN}/kubelet" \ --really_crash_for_testing=true \ --root_dir=/tmp/kubelet.$$ \ + --docker_endpoint="fake://" \ --etcd_servers="http://${ETCD_HOST}:${ETCD_PORT}" \ --hostname_override="127.0.0.1" \ --address="127.0.0.1" \ diff --git a/pkg/kubelet/dockertools/docker.go b/pkg/kubelet/dockertools/docker.go index 04115703c9d..dfe822fab19 100644 --- a/pkg/kubelet/dockertools/docker.go +++ b/pkg/kubelet/dockertools/docker.go @@ -25,6 +25,7 @@ import ( "io" "io/ioutil" "math/rand" + "os" "os/exec" "strconv" "strings" @@ -624,6 +625,34 @@ func parseImageName(image string) (string, string) { return image, tag } +// Get a docker endpoint, either from the string passed in, or $DOCKER_HOST environment variables +func getDockerEndpoint(dockerEndpoint string) string { + var endpoint string + if len(dockerEndpoint) > 0 { + endpoint = dockerEndpoint + } else if len(os.Getenv("DOCKER_HOST")) > 0 { + endpoint = os.Getenv("DOCKER_HOST") + } else { + endpoint = "unix:///var/run/docker.sock" + } + glog.Infof("Connecting to docker on %s", endpoint) + + return endpoint +} + +func ConnectToDockerOrDie(dockerEndpoint string) DockerInterface { + if dockerEndpoint == "fake://" { + return &FakeDockerClient{ + VersionInfo: []string{"apiVersion=1.16"}, + } + } + client, err := docker.NewClient(getDockerEndpoint(dockerEndpoint)) + if err != nil { + glog.Fatal("Couldn't connect to docker.") + } + return client +} + type ContainerCommandRunner interface { RunInContainer(containerID string, cmd []string) ([]byte, error) GetDockerServerVersion() ([]uint, error) diff --git a/pkg/kubelet/server.go b/pkg/kubelet/server.go index 6ac65dc280f..b6c7378ea45 100644 --- a/pkg/kubelet/server.go +++ b/pkg/kubelet/server.go @@ -115,7 +115,7 @@ func isValidDockerVersion(ver []uint) (bool, string) { if ver[i] != minAllowedVersion[i] { if ver[i] < minAllowedVersion[i] { versions := make([]string, len(ver)) - for i, v := range(ver) { + for i, v := range ver { versions[i] = fmt.Sprint(v) } return false, strings.Join(versions, ".") @@ -139,7 +139,7 @@ func (s *Server) handleHealthz(w http.ResponseWriter, req *http.Request) { w.WriteHeader(http.StatusInternalServerError) msg := "Docker version is too old (" + version + ")" w.Write([]byte(msg)) - return; + return } w.WriteHeader(http.StatusOK) w.Write([]byte("ok")) diff --git a/pkg/kubelet/server/server.go b/pkg/kubelet/server/server.go index 943e6da99c0..13860d6ad8e 100644 --- a/pkg/kubelet/server/server.go +++ b/pkg/kubelet/server/server.go @@ -203,7 +203,7 @@ func (s *KubeletServer) Run(_ []string) error { CAdvisorPort: s.CAdvisorPort, EnableServer: s.EnableServer, EnableDebuggingHandlers: s.EnableDebuggingHandlers, - DockerClient: util.ConnectToDockerOrDie(s.DockerEndpoint), + DockerClient: dockertools.ConnectToDockerOrDie(s.DockerEndpoint), KubeClient: client, EtcdClient: kubelet.EtcdClientOrDie(s.EtcdServerList, s.EtcdConfigFile), MasterServiceNamespace: s.MasterServiceNamespace, diff --git a/pkg/util/node.go b/pkg/util/node.go index e427394b166..4db7ae99772 100644 --- a/pkg/util/node.go +++ b/pkg/util/node.go @@ -17,11 +17,9 @@ limitations under the License. package util import ( - "os" "os/exec" "strings" - "github.com/fsouza/go-dockerclient" "github.com/golang/glog" ) @@ -38,26 +36,3 @@ func GetHostname(hostnameOverride string) string { } return strings.TrimSpace(string(hostname)) } - -// Get a docker endpoint, either from the string passed in, or $DOCKER_HOST environment variables -func GetDockerEndpoint(dockerEndpoint string) string { - var endpoint string - if len(dockerEndpoint) > 0 { - endpoint = dockerEndpoint - } else if len(os.Getenv("DOCKER_HOST")) > 0 { - endpoint = os.Getenv("DOCKER_HOST") - } else { - endpoint = "unix:///var/run/docker.sock" - } - glog.Infof("Connecting to docker on %s", endpoint) - - return endpoint -} - -func ConnectToDockerOrDie(dockerEndpoint string) *docker.Client { - client, err := docker.NewClient(GetDockerEndpoint(dockerEndpoint)) - if err != nil { - glog.Fatal("Couldn't connect to docker.") - } - return client -} From 1a3419f970e20e83c799fdccca81952080c3b765 Mon Sep 17 00:00:00 2001 From: Wojciech Tyczynski Date: Thu, 5 Feb 2015 02:34:04 +0100 Subject: [PATCH 19/50] Fix the min allowed Docker version. --- pkg/kubelet/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/kubelet/server.go b/pkg/kubelet/server.go index b6c7378ea45..35953a15bf8 100644 --- a/pkg/kubelet/server.go +++ b/pkg/kubelet/server.go @@ -110,7 +110,7 @@ func (s *Server) error(w http.ResponseWriter, err error) { } func isValidDockerVersion(ver []uint) (bool, string) { - minAllowedVersion := []uint{1, 3, 0} + minAllowedVersion := []uint{1, 15} for i := 0; i < len(ver) && i < len(minAllowedVersion); i++ { if ver[i] != minAllowedVersion[i] { if ver[i] < minAllowedVersion[i] { From 19f7ecf31b6f17685bb9a96f1a827fc1f5387918 Mon Sep 17 00:00:00 2001 From: nikhiljindal Date: Wed, 4 Feb 2015 20:50:45 -0800 Subject: [PATCH 20/50] Deleting the API objects related to /operations --- pkg/api/register.go | 6 ------ pkg/api/types.go | 14 -------------- pkg/api/v1beta1/register.go | 6 ------ pkg/api/v1beta1/types.go | 11 ----------- pkg/api/v1beta2/register.go | 6 ------ pkg/api/v1beta2/types.go | 11 ----------- pkg/api/v1beta3/register.go | 6 ------ pkg/api/v1beta3/types.go | 16 ---------------- pkg/apiserver/operation.go | 18 ------------------ 9 files changed, 94 deletions(-) diff --git a/pkg/api/register.go b/pkg/api/register.go index b8638e442c2..8901111bb31 100644 --- a/pkg/api/register.go +++ b/pkg/api/register.go @@ -35,8 +35,6 @@ func init() { &NodeList{}, &Node{}, &Status{}, - &OperationList{}, - &Operation{}, &Endpoints{}, &EndpointsList{}, &Binding{}, @@ -56,8 +54,6 @@ func init() { // Legacy names are supported Scheme.AddKnownTypeWithName("", "Minion", &Node{}) Scheme.AddKnownTypeWithName("", "MinionList", &NodeList{}) - Scheme.AddKnownTypeWithName("", "ServerOp", &Operation{}) - Scheme.AddKnownTypeWithName("", "ServerOpList", &OperationList{}) } func (*Pod) IsAnAPIObject() {} @@ -73,8 +69,6 @@ func (*Node) IsAnAPIObject() {} func (*NodeList) IsAnAPIObject() {} func (*Binding) IsAnAPIObject() {} func (*Status) IsAnAPIObject() {} -func (*Operation) IsAnAPIObject() {} -func (*OperationList) IsAnAPIObject() {} func (*Event) IsAnAPIObject() {} func (*EventList) IsAnAPIObject() {} func (*ContainerManifest) IsAnAPIObject() {} diff --git a/pkg/api/types.go b/pkg/api/types.go index f78b1d7d460..fabb20f0a4c 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -1028,20 +1028,6 @@ const ( CauseTypeFieldValueNotSupported CauseType = "FieldValueNotSupported" ) -// Operation is an operation delivered to API clients. -type Operation struct { - TypeMeta `json:",inline"` - ObjectMeta `json:"metadata,omitempty"` -} - -// OperationList is a list of operations, as delivered to API clients. -type OperationList struct { - TypeMeta `json:",inline"` - ListMeta `json:"metadata,omitempty"` - - Items []Operation `json:"items"` -} - // ObjectReference contains enough information to let you inspect or modify the referred object. type ObjectReference struct { Kind string `json:"kind,omitempty"` diff --git a/pkg/api/v1beta1/register.go b/pkg/api/v1beta1/register.go index 17c917a4860..d3f8a91a651 100644 --- a/pkg/api/v1beta1/register.go +++ b/pkg/api/v1beta1/register.go @@ -39,8 +39,6 @@ func init() { &MinionList{}, &Binding{}, &Status{}, - &ServerOp{}, - &ServerOpList{}, &Event{}, &EventList{}, &ContainerManifest{}, @@ -57,8 +55,6 @@ func init() { // Future names are supported api.Scheme.AddKnownTypeWithName("v1beta1", "Node", &Minion{}) api.Scheme.AddKnownTypeWithName("v1beta1", "NodeList", &MinionList{}) - api.Scheme.AddKnownTypeWithName("v1beta1", "Operation", &ServerOp{}) - api.Scheme.AddKnownTypeWithName("v1beta1", "OperationList", &ServerOpList{}) } func (*Pod) IsAnAPIObject() {} @@ -74,8 +70,6 @@ func (*Minion) IsAnAPIObject() {} func (*MinionList) IsAnAPIObject() {} func (*Binding) IsAnAPIObject() {} func (*Status) IsAnAPIObject() {} -func (*ServerOp) IsAnAPIObject() {} -func (*ServerOpList) IsAnAPIObject() {} func (*Event) IsAnAPIObject() {} func (*EventList) IsAnAPIObject() {} func (*ContainerManifest) IsAnAPIObject() {} diff --git a/pkg/api/v1beta1/types.go b/pkg/api/v1beta1/types.go index 6efaea08f14..9f829a92b6d 100644 --- a/pkg/api/v1beta1/types.go +++ b/pkg/api/v1beta1/types.go @@ -808,17 +808,6 @@ const ( CauseTypeFieldValueNotSupported CauseType = "FieldValueNotSupported" ) -// ServerOp is an operation delivered to API clients. -type ServerOp struct { - TypeMeta `json:",inline"` -} - -// ServerOpList is a list of operations, as delivered to API clients. -type ServerOpList struct { - TypeMeta `json:",inline"` - Items []ServerOp `json:"items" description:"list of operations"` -} - // ObjectReference contains enough information to let you inspect or modify the referred object. type ObjectReference struct { Kind string `json:"kind,omitempty" description:"kind of the referent"` diff --git a/pkg/api/v1beta2/register.go b/pkg/api/v1beta2/register.go index ad447476c23..88fe96e700d 100644 --- a/pkg/api/v1beta2/register.go +++ b/pkg/api/v1beta2/register.go @@ -39,8 +39,6 @@ func init() { &MinionList{}, &Binding{}, &Status{}, - &ServerOp{}, - &ServerOpList{}, &Event{}, &EventList{}, &ContainerManifest{}, @@ -57,8 +55,6 @@ func init() { // Future names are supported api.Scheme.AddKnownTypeWithName("v1beta2", "Node", &Minion{}) api.Scheme.AddKnownTypeWithName("v1beta2", "NodeList", &MinionList{}) - api.Scheme.AddKnownTypeWithName("v1beta2", "Operation", &ServerOp{}) - api.Scheme.AddKnownTypeWithName("v1beta2", "OperationList", &ServerOpList{}) } func (*Pod) IsAnAPIObject() {} @@ -74,8 +70,6 @@ func (*Minion) IsAnAPIObject() {} func (*MinionList) IsAnAPIObject() {} func (*Binding) IsAnAPIObject() {} func (*Status) IsAnAPIObject() {} -func (*ServerOp) IsAnAPIObject() {} -func (*ServerOpList) IsAnAPIObject() {} func (*Event) IsAnAPIObject() {} func (*EventList) IsAnAPIObject() {} func (*ContainerManifest) IsAnAPIObject() {} diff --git a/pkg/api/v1beta2/types.go b/pkg/api/v1beta2/types.go index fe52e819285..ade358961b4 100644 --- a/pkg/api/v1beta2/types.go +++ b/pkg/api/v1beta2/types.go @@ -782,17 +782,6 @@ const ( CauseTypeFieldValueNotSupported CauseType = "FieldValueNotSupported" ) -// ServerOp is an operation delivered to API clients. -type ServerOp struct { - TypeMeta `json:",inline"` -} - -// ServerOpList is a list of operations, as delivered to API clients. -type ServerOpList struct { - TypeMeta `json:",inline"` - Items []ServerOp `json:"items" description:"list of operations"` -} - // ObjectReference contains enough information to let you inspect or modify the referred object. type ObjectReference struct { Kind string `json:"kind,omitempty" description:"kind of the referent"` diff --git a/pkg/api/v1beta3/register.go b/pkg/api/v1beta3/register.go index a1956cdc67b..da801ab73b0 100644 --- a/pkg/api/v1beta3/register.go +++ b/pkg/api/v1beta3/register.go @@ -43,8 +43,6 @@ func init() { &NodeList{}, &Binding{}, &Status{}, - &Operation{}, - &OperationList{}, &Event{}, &EventList{}, &List{}, @@ -57,8 +55,6 @@ func init() { // Legacy names are supported api.Scheme.AddKnownTypeWithName("v1beta3", "Minion", &Node{}) api.Scheme.AddKnownTypeWithName("v1beta3", "MinionList", &NodeList{}) - api.Scheme.AddKnownTypeWithName("v1beta3", "ServerOp", &Operation{}) - api.Scheme.AddKnownTypeWithName("v1beta3", "ServerOpList", &OperationList{}) } func (*Pod) IsAnAPIObject() {} @@ -78,8 +74,6 @@ func (*Node) IsAnAPIObject() {} func (*NodeList) IsAnAPIObject() {} func (*Binding) IsAnAPIObject() {} func (*Status) IsAnAPIObject() {} -func (*Operation) IsAnAPIObject() {} -func (*OperationList) IsAnAPIObject() {} func (*Event) IsAnAPIObject() {} func (*EventList) IsAnAPIObject() {} func (*List) IsAnAPIObject() {} diff --git a/pkg/api/v1beta3/types.go b/pkg/api/v1beta3/types.go index 5b24abc1a88..88dceecea78 100644 --- a/pkg/api/v1beta3/types.go +++ b/pkg/api/v1beta3/types.go @@ -1008,22 +1008,6 @@ const ( CauseTypeFieldValueNotSupported CauseType = "FieldValueNotSupported" ) -// Operation is a request from a client that has not yet been satisfied. The name of an -// Operation is assigned by the server when an operation is started, and can be used by -// clients to retrieve the final result of the operation at a later time. -type Operation struct { - TypeMeta `json:",inline"` - ObjectMeta `json:"metadata"` -} - -// OperationList is a list of operations, as delivered to API clients. -type OperationList struct { - TypeMeta `json:",inline"` - ListMeta `json:"metadata,omitempty"` - - Items []Operation `json:"items"` -} - // ObjectReference contains enough information to let you inspect or modify the referred object. type ObjectReference struct { Kind string `json:"kind,omitempty"` diff --git a/pkg/apiserver/operation.go b/pkg/apiserver/operation.go index 8d073499472..f0c88de6cfb 100644 --- a/pkg/apiserver/operation.go +++ b/pkg/apiserver/operation.go @@ -17,7 +17,6 @@ limitations under the License. package apiserver import ( - "sort" "strconv" "sync" "sync/atomic" @@ -79,23 +78,6 @@ func (ops *Operations) insert(op *Operation) { ops.ops[op.ID] = op } -// List lists operations for an API client. -func (ops *Operations) List() *api.OperationList { - ops.lock.Lock() - defer ops.lock.Unlock() - - ids := []string{} - for id := range ops.ops { - ids = append(ids, id) - } - sort.StringSlice(ids).Sort() - ol := &api.OperationList{} - for _, id := range ids { - ol.Items = append(ol.Items, api.Operation{ObjectMeta: api.ObjectMeta{Name: id}}) - } - return ol -} - // Get returns the operation with the given ID, or nil. func (ops *Operations) Get(id string) *Operation { ops.lock.Lock() From 30f91b0ab73531295657d7453abe67a0c75dba47 Mon Sep 17 00:00:00 2001 From: Brendan Burns Date: Tue, 3 Feb 2015 12:23:08 -0800 Subject: [PATCH 21/50] Add a simple endpoints test. --- test/e2e/endpoints.go | 196 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 test/e2e/endpoints.go diff --git a/test/e2e/endpoints.go b/test/e2e/endpoints.go new file mode 100644 index 00000000000..19c220077b3 --- /dev/null +++ b/test/e2e/endpoints.go @@ -0,0 +1,196 @@ +/* +Copyright 2014 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "net" + "time" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/client" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" + "github.com/golang/glog" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func validateIPs(c *client.Client, ns, expectedPort string, expectedEndpoints []string, endpoints *api.Endpoints) bool { + ips := util.StringSet{} + for _, spec := range endpoints.Endpoints { + host, port, err := net.SplitHostPort(spec) + if err != nil { + glog.Warningf("invalid endpoint spec: %s", spec) + return false + } + if port != expectedPort { + glog.Warningf("invalid port, expected %s, got %s", expectedPort, port) + return false + } + ips.Insert(host) + } + + for _, name := range expectedEndpoints { + pod, err := c.Pods(ns).Get(name) + if err != nil { + glog.Warningf("failed to get pod %s, that's pretty weird. validation failed.", name) + return false + } + if !ips.Has(pod.Status.PodIP) { + glog.Warningf("ip validation failed, expected: %v, saw: %v", ips, pod.Status.PodIP) + return false + } + } + return true +} + +func validateEndpoints(c *client.Client, ns, serviceName, expectedPort string, expectedEndpoints []string) bool { + done := make(chan bool) + running := true + go func() { + ok := true + for running { + endpoints, err := c.Endpoints(ns).Get(serviceName) + if err == nil { + if len(endpoints.Endpoints) != len(expectedEndpoints) { + glog.Warningf("Unexpected endpoints: %#v, expected %v", endpoints, expectedEndpoints) + } else if validateIPs(c, ns, expectedPort, expectedEndpoints, endpoints) { + break + } + } else { + glog.Warningf("Failed to get endpoints: %v", err) + } + time.Sleep(time.Second) + } + done <- ok + }() + + select { + case result := <-done: + return result + case <-time.After(60 * time.Second): + glog.Errorf("Timed out waiting for endpoints.") + running = false + return false + } +} + +func addPod(c *client.Client, ns, name string, labels map[string]string) error { + pod := &api.Pod{ + ObjectMeta: api.ObjectMeta{ + Name: name, + Labels: labels, + }, + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: "test", + Image: "kubernetes/pause", + Ports: []api.Port{{ContainerPort: 80}}, + }, + }, + }, + } + _, err := c.Pods(ns).Create(pod) + return err +} + +func TestEndpoints(c *client.Client) bool { + serviceName := "endpoint-test" + ns := api.NamespaceDefault + labels := map[string]string{ + "foo": "bar", + "baz": "blah", + } + + defer func() { + err := c.Services(ns).Delete(serviceName) + if err != nil { + glog.Errorf("Failed to delete service: %v (%v)", serviceName, err) + } + }() + + service := &api.Service{ + ObjectMeta: api.ObjectMeta{ + Name: serviceName, + }, + Spec: api.ServiceSpec{ + Port: 80, + Selector: labels, + ContainerPort: util.NewIntOrStringFromInt(80), + }, + } + if _, err := c.Services(ns).Create(service); err != nil { + glog.Errorf("Failed to create endpoint test service: %v", err) + return false + } + expectedPort := "80" + + if !validateEndpoints(c, ns, serviceName, expectedPort, []string{}) { + return false + } + + name1 := "test1" + if err := addPod(c, ns, name1, labels); err != nil { + glog.Errorf("Failed to create pod: %v", err) + return false + } + names := []string{name1} + defer func() { + for _, name := range names { + err := c.Pods(ns).Delete(name) + if err != nil { + glog.Errorf("Failed to delete pod: %v (%v)", name, err) + } + } + }() + + if !validateEndpoints(c, ns, serviceName, expectedPort, names) { + return false + } + + name2 := "test2" + if err := addPod(c, ns, name2, labels); err != nil { + glog.Errorf("Failed to create pod: %v", err) + return false + } + names = append(names, name2) + + if !validateEndpoints(c, ns, serviceName, expectedPort, names) { + return false + } + + if err := c.Pods(ns).Delete(name1); err != nil { + glog.Errorf("Failed to delete pod: %s", name1) + return false + } + names = []string{name2} + + if !validateEndpoints(c, ns, serviceName, expectedPort, names) { + return false + } + + return true +} + +var _ = Describe("TestEndpoints", func() { + It("should pass", func() { + // TODO: Instead of OrDie, client should Fail the test if there's a problem. + // In general tests should Fail() instead of glog.Fatalf(). + Expect(TestEndpoints(loadClientOrDie())).To(BeTrue()) + }) +}) From 93776b1363cf914e1ab8d18c7254bb730775a6fd Mon Sep 17 00:00:00 2001 From: Brendan Burns Date: Wed, 4 Feb 2015 23:06:03 -0800 Subject: [PATCH 22/50] Remove kubecfg from all getting started docs. --- build/README.md | 2 +- docs/getting-started-guides/aws-coreos.md | 18 ++++++------ docs/getting-started-guides/aws/kubecfg.md | 28 ------------------- docs/getting-started-guides/aws/kubectl.md | 28 +++++++++++++++++++ docs/getting-started-guides/azure.md | 21 ++++++++------ docs/getting-started-guides/gce.md | 10 +++---- docs/getting-started-guides/locally.md | 6 ++-- .../ubuntu_single_node.md | 2 +- docs/getting-started-guides/vagrant.md | 7 ++--- 9 files changed, 63 insertions(+), 59 deletions(-) delete mode 100644 docs/getting-started-guides/aws/kubecfg.md create mode 100644 docs/getting-started-guides/aws/kubectl.md diff --git a/build/README.md b/build/README.md index b583f3ba323..c02146d8f41 100644 --- a/build/README.md +++ b/build/README.md @@ -34,7 +34,7 @@ The `release.sh` script will build a release. It will build binaries, run tests The main output is a tar file: `kubernetes.tar.gz`. This includes: * Cross compiled client utilities. -* Script (`cluster/kubecfg.sh`) for picking and running the right client binary based on platform. +* Script (`cluster/kubectl.sh`) for picking and running the right client binary based on platform. * Examples * Cluster deployment scripts for various clouds * Tar file containing all server binaries diff --git a/docs/getting-started-guides/aws-coreos.md b/docs/getting-started-guides/aws-coreos.md index 240b61a6fa4..83132c1bf7f 100644 --- a/docs/getting-started-guides/aws-coreos.md +++ b/docs/getting-started-guides/aws-coreos.md @@ -14,7 +14,7 @@ The example below creates an elastic Kubernetes cluster with 3 worker nodes and ## Prerequisites -* [kubecfg CLI](aws/kubecfg.md) +* [kubectl CLI](aws/kubectl.md) * [aws CLI](http://aws.amazon.com/cli) * [CoreOS image for AWS](https://coreos.com/docs/running-coreos/cloud-providers/ec2/#choosing-a-channel) @@ -42,7 +42,7 @@ aws cloudformation describe-stack-events --stack-name kubernetes aws cloudformation describe-stacks --stack-name kubernetes ``` -[Skip to kubecfg client configuration](#configure-the-kubecfg-ssh-tunnel) +[Skip to kubectl client configuration](#configure-the-kubectl-ssh-tunnel) ### Manually @@ -121,9 +121,9 @@ aws ec2 run-instances --count 1 --image-id --key-name \ --user-data file://node.yaml ``` -### Configure the kubecfg SSH tunnel +### Configure the kubectl SSH tunnel -This command enables secure communication between the kubecfg client and the Kubernetes API. +This command enables secure communication between the kubectl client and the Kubernetes API. ``` ssh -f -nNT -L 8080:127.0.0.1:8080 core@ @@ -134,7 +134,7 @@ ssh -f -nNT -L 8080:127.0.0.1:8080 core@ Once the worker instances have fully booted, they will be automatically registered with the Kubernetes API server by the kube-register service running on the master node. It may take a few mins. ``` -kubecfg list minions +kubectl get nodes ``` ## Starting a simple pod @@ -167,16 +167,16 @@ Create a pod manifest: `pod.json` } ``` -### Create the pod using the kubecfg command line tool +### Create the pod using the kubectl command line tool ``` -kubecfg -c pod.json create pods +kubectl create -f pod.json ``` ### Testing ``` -kubecfg list pods +kubectl get pods ``` > Record the **Host** of the pod, which should be the private IP address. @@ -208,5 +208,5 @@ Visit the public IP address in your browser to view the running pod. ### Delete the pod ``` -kubecfg delete pods/hello +kubectl delete pods hello ``` diff --git a/docs/getting-started-guides/aws/kubecfg.md b/docs/getting-started-guides/aws/kubecfg.md deleted file mode 100644 index 2b6a24e0618..00000000000 --- a/docs/getting-started-guides/aws/kubecfg.md +++ /dev/null @@ -1,28 +0,0 @@ -# Install and configure kubecfg - -## Download the kubecfg CLI tool - -### Darwin - -``` -wget http://storage.googleapis.com/k8s/darwin/kubecfg -``` - -### Linux - -``` -wget http://storage.googleapis.com/k8s/linux/kubecfg -``` - -### Copy kubecfg to your path - -``` -chmod +x kubecfg -mv kubecfg /usr/local/bin/ -``` - -### Create a secure tunnel for API communication - -``` -ssh -f -nNT -L 8080:127.0.0.1:8080 core@ -``` diff --git a/docs/getting-started-guides/aws/kubectl.md b/docs/getting-started-guides/aws/kubectl.md new file mode 100644 index 00000000000..0f93d2c46d6 --- /dev/null +++ b/docs/getting-started-guides/aws/kubectl.md @@ -0,0 +1,28 @@ +# Install and configure kubecfg + +## Download the kubecfg CLI tool + +### Darwin + +``` +wget https://storage.googleapis.com/kubernetes-release/release/v0.9.2/bin/darwin/amd64/kubectl +``` + +### Linux + +``` +wget https://storage.googleapis.com/kubernetes-release/release/v0.9.2/bin/linux/amd64/kubectl +``` + +### Copy kubectl to your path + +``` +chmod +x kubectl +mv kubectl /usr/local/bin/ +``` + +### Create a secure tunnel for API communication + +``` +ssh -f -nNT -L 8080:127.0.0.1:8080 core@ +``` diff --git a/docs/getting-started-guides/azure.md b/docs/getting-started-guides/azure.md index 1d9c75ac373..4eb1ef51cde 100644 --- a/docs/getting-started-guides/azure.md +++ b/docs/getting-started-guides/azure.md @@ -34,21 +34,26 @@ can tweak some of these parameters by editing `cluster/azure/config-default.sh`. ### Running a container (simple version) -The `cluster/kubecfg.sh` command below spins up two containers, running [Nginx](http://nginx.org/en/) and with port 80 mapped to 8080: +Once you have your instances up and running, the `hack/build-go.sh` script sets up +your Go workspace and builds the Go components. -``` -cd kubernetes -cluster/kubecfg.sh -p 8080:80 run dockerfile/nginx 2 myNginx +The `kubectl.sh` line below spins up two containers running +[Nginx](http://nginx.org/en/) running on port 80: + +```bash +cluster/kubectl.sh run-container my-nginx --image=dockerfile/nginx --replicas=2 --port=80 ``` To stop the containers: -``` -cluster/kubecfg.sh stop myNginx + +```bash +cluster/kubectl.sh stop rc my-nginx ``` To delete the containers: -``` -cluster/kubecfg.sh rm myNginx + +```bash +cluster/kubectl.sh delete rc my-nginx ``` ### Running a container (more complete version) diff --git a/docs/getting-started-guides/gce.md b/docs/getting-started-guides/gce.md index 7c6c368dd7c..4cb4ce19905 100644 --- a/docs/getting-started-guides/gce.md +++ b/docs/getting-started-guides/gce.md @@ -50,23 +50,23 @@ field values: Once you have your instances up and running, the `hack/build-go.sh` script sets up your Go workspace and builds the Go components. -The `kubecfg.sh` line below spins up two containers running -[Nginx](http://nginx.org/en/) with port 80 mapped to 8080: +The `kubectl.sh` line below spins up two containers running +[Nginx](http://nginx.org/en/) running on port 80: ```bash -cluster/kubecfg.sh -p 8080:80 run dockerfile/nginx 2 myNginx +cluster/kubectl.sh run-container my-nginx --image=dockerfile/nginx --replicas=2 --port=80 ``` To stop the containers: ```bash -cluster/kubecfg.sh stop myNginx +cluster/kubectl.sh stop rc my-nginx ``` To delete the containers: ```bash -cluster/kubecfg.sh rm myNginx +cluster/kubectl.sh delete rc my-nginx ``` ### Running a container (more complete version) diff --git a/docs/getting-started-guides/locally.md b/docs/getting-started-guides/locally.md index a7120f42f3a..40e6cf6fd60 100644 --- a/docs/getting-started-guides/locally.md +++ b/docs/getting-started-guides/locally.md @@ -30,7 +30,7 @@ hack/local-up-cluster.sh This will build and start a lightweight local cluster, consisting of a master and a single minion. Type Control-C to shut it down. -You can use the cluster/kubecfg.sh script to interact with the local cluster. +You can use the cluster/kubectl.sh script to interact with the local cluster. You must set the KUBERNETES_PROVIDER and KUBERNETES_MASTER environment variables to let other programs know how to reach your master. @@ -43,13 +43,13 @@ export KUBERNETES_MASTER=http://localhost:8080 Your cluster is running, and you want to start running containers! -You can now use any of the cluster/kubecfg.sh commands to interact with your local setup. +You can now use any of the cluster/kubectl.sh commands to interact with your local setup. ``` cluster/kubectl.sh get pods cluster/kubectl.sh get services cluster/kubectl.sh get replicationControllers -cluster/kubecfg.sh -p 8081:80 run dockerfile/nginx 1 myNginx +cluster/kubectl.sh run-container my-nginx --image=dockerfile/nginx --replicas=2 --port=80 ## begin wait for provision to complete, you can monitor the docker pull by opening a new terminal diff --git a/docs/getting-started-guides/ubuntu_single_node.md b/docs/getting-started-guides/ubuntu_single_node.md index b244eb1275f..a84f11b7dd5 100644 --- a/docs/getting-started-guides/ubuntu_single_node.md +++ b/docs/getting-started-guides/ubuntu_single_node.md @@ -25,7 +25,7 @@ $ sudo ./util.sh After this the kubernetes and `etcd` services would be up and running. You can use `service start/stop/restart/force-reload` on the services. -Launching and scheduling containers using kubecfg can also be used at this point, as explained mentioned in the [examples](https://github.com/GoogleCloudPlatform/kubernetes/tree/master/examples/guestbook) +Launching and scheduling containers using kubectl can also be used at this point, as explained mentioned in the [examples](https://github.com/GoogleCloudPlatform/kubernetes/tree/master/examples/guestbook) ### 3. Customizing the ubuntu launch To customize the defaults you will need to tweak `/etc/default/kube*` files and restart the appropriate services. This is needed if the binaries are copied in a place other than `/opt/bin`. A run could look like diff --git a/docs/getting-started-guides/vagrant.md b/docs/getting-started-guides/vagrant.md index 4d27fd572ed..8f7ab7ba786 100644 --- a/docs/getting-started-guides/vagrant.md +++ b/docs/getting-started-guides/vagrant.md @@ -178,10 +178,9 @@ NAME IMAGE(S SELECTOR REPLICAS ``` Start a container running nginx with a replication controller and three replicas -(note that this step uses the `kubecfg.sh` command instead of `kubectl.sh`): ``` -$ cluster/kubecfg.sh -p 8080:80 run dockerfile/nginx 3 myNginx +$ cluster/kubectl.sh run-container my-nginx --image=dockerfile/nginx --replicas=3 --port=80 ``` When listing the pods, you will see that three containers have been started and are in Waiting state: @@ -231,7 +230,7 @@ NAME LABELS SELECTOR IP PORT $ cluster/kubectl.sh get replicationControllers NAME IMAGE(S SELECTOR REPLICAS -myNginx dockerfile/nginx name=myNginx 3 +myNginx dockerfile/nginx name=my-nginx 3 ``` We did not start any services, hence there are none listed. But we see three replicas displayed properly. @@ -239,7 +238,7 @@ Check the [guestbook](../../examples/guestbook/README.md) application to learn h You can already play with resizing the replicas with: ``` -$ cluster/kubecfg.sh resize myNginx 2 +$ cluster/kubectl.sh resize rc my-nginx --replicas=2 $ cluster/kubectl.sh get pods NAME IMAGE(S) HOST LABELS STATUS 7813c8bd-3ffe-11e4-9036-0800279696e1 dockerfile/nginx 10.245.2.2/10.245.2.2 name=myNginx Running From a41f520bf0fad18a52cabc702d204e78758e09ab Mon Sep 17 00:00:00 2001 From: saadali Date: Thu, 5 Feb 2015 00:05:36 -0800 Subject: [PATCH 23/50] Add "Update Event" to Kubernetes API --- pkg/api/errors/etcd/etcd.go | 4 +- pkg/registry/etcd/etcd.go | 8 +- pkg/registry/event/registry.go | 11 ++ pkg/registry/event/registry_test.go | 102 +++++++++++++++++++ pkg/registry/event/rest.go | 25 +++++ pkg/registry/event/rest_test.go | 31 ++++++ pkg/registry/generic/etcd/etcd.go | 2 +- pkg/tools/etcd_tools.go | 10 +- pkg/tools/etcd_tools_test.go | 16 ++- pkg/tools/fake_etcd_client.go | 1 + test/integration/auth_test.go | 153 ++++++++++++++++++++++++---- test/integration/etcd_tools_test.go | 2 +- 12 files changed, 330 insertions(+), 35 deletions(-) diff --git a/pkg/api/errors/etcd/etcd.go b/pkg/api/errors/etcd/etcd.go index dee022828f5..379b7873afe 100644 --- a/pkg/api/errors/etcd/etcd.go +++ b/pkg/api/errors/etcd/etcd.go @@ -43,7 +43,7 @@ func InterpretCreateError(err error, kind, name string) error { } } -// InterpretUpdateError converts a generic etcd error on a create +// InterpretUpdateError converts a generic etcd error on a update // operation into the appropriate API error. func InterpretUpdateError(err error, kind, name string) error { switch { @@ -54,7 +54,7 @@ func InterpretUpdateError(err error, kind, name string) error { } } -// InterpretDeleteError converts a generic etcd error on a create +// InterpretDeleteError converts a generic etcd error on a delete // operation into the appropriate API error. func InterpretDeleteError(err error, kind, name string) error { switch { diff --git a/pkg/registry/etcd/etcd.go b/pkg/registry/etcd/etcd.go index d2bd576cb52..76ae2204a2f 100644 --- a/pkg/registry/etcd/etcd.go +++ b/pkg/registry/etcd/etcd.go @@ -251,7 +251,7 @@ func (r *Registry) UpdatePod(ctx api.Context, pod *api.Pod) error { } // There's no race with the scheduler, because either this write will fail because the host // has been updated, or the host update will fail because this pod has been updated. - err = r.EtcdHelper.SetObj(podKey, pod) + err = r.EtcdHelper.SetObj(podKey, pod, 0 /* ttl */) if err != nil { return err } @@ -404,7 +404,7 @@ func (r *Registry) UpdateController(ctx api.Context, controller *api.Replication if err != nil { return err } - err = r.SetObj(key, controller) + err = r.SetObj(key, controller, 0 /* ttl */) return etcderr.InterpretUpdateError(err, "replicationController", controller.Name) } @@ -512,7 +512,7 @@ func (r *Registry) UpdateService(ctx api.Context, svc *api.Service) error { if err != nil { return err } - err = r.SetObj(key, svc) + err = r.SetObj(key, svc, 0 /* ttl */) return etcderr.InterpretUpdateError(err, "service", svc.Name) } @@ -605,7 +605,7 @@ func (r *Registry) CreateMinion(ctx api.Context, minion *api.Node) error { func (r *Registry) UpdateMinion(ctx api.Context, minion *api.Node) error { // TODO: Add some validations. - err := r.SetObj(makeNodeKey(minion.Name), minion) + err := r.SetObj(makeNodeKey(minion.Name), minion, 0 /* ttl */) return etcderr.InterpretUpdateError(err, "minion", minion.Name) } diff --git a/pkg/registry/event/registry.go b/pkg/registry/event/registry.go index 247639158e2..47436046b75 100644 --- a/pkg/registry/event/registry.go +++ b/pkg/registry/event/registry.go @@ -41,6 +41,17 @@ func (r registry) Create(ctx api.Context, id string, obj runtime.Object) error { return etcderr.InterpretCreateError(err, r.Etcd.EndpointName, id) } +// Update replaces an existing instance of the object, and sets a ttl so that the event +// doesn't stay in the system forever. +func (r registry) Update(ctx api.Context, id string, obj runtime.Object) error { + key, err := r.Etcd.KeyFunc(ctx, id) + if err != nil { + return err + } + err = r.Etcd.Helper.SetObj(key, obj, r.ttl) + return etcderr.InterpretUpdateError(err, r.Etcd.EndpointName, id) +} + // NewEtcdRegistry returns a registry which will store Events in the given // EtcdHelper. ttl is the time that Events will be retained by the system. func NewEtcdRegistry(h tools.EtcdHelper, ttl uint64) generic.Registry { diff --git a/pkg/registry/event/registry_test.go b/pkg/registry/event/registry_test.go index d85f23d23d3..3389af8cae6 100644 --- a/pkg/registry/event/registry_test.go +++ b/pkg/registry/event/registry_test.go @@ -108,3 +108,105 @@ func TestEventCreate(t *testing.T) { } } } + +func TestEventUpdate(t *testing.T) { + eventA := &api.Event{ + ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: api.NamespaceDefault}, + Reason: "forTesting", + } + eventB := &api.Event{ + ObjectMeta: api.ObjectMeta{Name: "bar", Namespace: api.NamespaceDefault}, + Reason: "for testing again", + } + eventC := &api.Event{ + ObjectMeta: api.ObjectMeta{Name: "pan", Namespace: api.NamespaceDefault, ResourceVersion: "1"}, + Reason: "for testing again something else", + } + + nodeWithEventA := tools.EtcdResponseWithError{ + R: &etcd.Response{ + Node: &etcd.Node{ + Value: runtime.EncodeOrDie(testapi.Codec(), eventA), + ModifiedIndex: 1, + CreatedIndex: 1, + TTL: int64(testTTL), + }, + }, + E: nil, + } + + nodeWithEventB := tools.EtcdResponseWithError{ + R: &etcd.Response{ + Node: &etcd.Node{ + Value: runtime.EncodeOrDie(testapi.Codec(), eventB), + ModifiedIndex: 1, + CreatedIndex: 1, + TTL: int64(testTTL), + }, + }, + E: nil, + } + + nodeWithEventC := tools.EtcdResponseWithError{ + R: &etcd.Response{ + Node: &etcd.Node{ + Value: runtime.EncodeOrDie(testapi.Codec(), eventC), + ModifiedIndex: 1, + CreatedIndex: 1, + TTL: int64(testTTL), + }, + }, + E: nil, + } + + emptyNode := tools.EtcdResponseWithError{ + R: &etcd.Response{}, + E: tools.EtcdErrorNotFound, + } + + ctx := api.NewDefaultContext() + key := "foo" + path, err := etcdgeneric.NamespaceKeyFunc(ctx, "/registry/events", key) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + table := map[string]struct { + existing tools.EtcdResponseWithError + expect tools.EtcdResponseWithError + toUpdate runtime.Object + errOK func(error) bool + }{ + "doesNotExist": { + existing: emptyNode, + expect: nodeWithEventA, + toUpdate: eventA, + errOK: func(err error) bool { return err == nil }, + }, + "doesNotExist2": { + existing: emptyNode, + expect: nodeWithEventB, + toUpdate: eventB, + errOK: func(err error) bool { return err == nil }, + }, + "replaceExisting": { + existing: nodeWithEventA, + expect: nodeWithEventC, + toUpdate: eventC, + errOK: func(err error) bool { return err == nil }, + }, + } + + for name, item := range table { + fakeClient, registry := NewTestEventEtcdRegistry(t) + fakeClient.Data[path] = item.existing + err := registry.Update(ctx, key, item.toUpdate) + if !item.errOK(err) { + t.Errorf("%v: unexpected error: %v", name, err) + } + + if e, a := item.expect, fakeClient.Data[path]; !reflect.DeepEqual(e, a) { + t.Errorf("%v:\n%s", name, util.ObjectGoPrintDiff(e, a)) + } + } +} diff --git a/pkg/registry/event/rest.go b/pkg/registry/event/rest.go index b3651163843..b736c466beb 100644 --- a/pkg/registry/event/rest.go +++ b/pkg/registry/event/rest.go @@ -66,6 +66,31 @@ func (rs *REST) Create(ctx api.Context, obj runtime.Object) (<-chan apiserver.RE }), nil } +// Update replaces an existing Event instance in storage.registry, with the given instance. +func (rs *REST) Update(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) { + event, ok := obj.(*api.Event) + if !ok { + return nil, fmt.Errorf("not an event object: %#v", obj) + } + if api.Namespace(ctx) != "" { + if !api.ValidNamespace(ctx, &event.ObjectMeta) { + return nil, errors.NewConflict("event", event.Namespace, fmt.Errorf("event.namespace does not match the provided context")) + } + } + if errs := validation.ValidateEvent(event); len(errs) > 0 { + return nil, errors.NewInvalid("event", event.Name, errs) + } + api.FillObjectMetaSystemFields(ctx, &event.ObjectMeta) + + return apiserver.MakeAsync(func() (runtime.Object, error) { + err := rs.registry.Update(ctx, event.Name, event) + if err != nil { + return nil, err + } + return rs.registry.Get(ctx, event.Name) + }), nil +} + func (rs *REST) Delete(ctx api.Context, id string) (<-chan apiserver.RESTResult, error) { obj, err := rs.registry.Get(ctx, id) if err != nil { diff --git a/pkg/registry/event/rest_test.go b/pkg/registry/event/rest_test.go index c2b60b499df..f59feb993ae 100644 --- a/pkg/registry/event/rest_test.go +++ b/pkg/registry/event/rest_test.go @@ -97,6 +97,37 @@ func TestRESTCreate(t *testing.T) { } } +func TestRESTUpdate(t *testing.T) { + _, rest := NewTestREST() + eventA := testEvent("foo") + c, err := rest.Create(api.NewDefaultContext(), eventA) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + <-c + got, err := rest.Get(api.NewDefaultContext(), eventA.Name) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + if e, a := eventA, got; !reflect.DeepEqual(e, a) { + t.Errorf("diff: %s", util.ObjectDiff(e, a)) + } + eventB := testEvent("bar") + u, err := rest.Update(api.NewDefaultContext(), eventB) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + <-u + got2, err := rest.Get(api.NewDefaultContext(), eventB.Name) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + if e, a := eventB, got2; !reflect.DeepEqual(e, a) { + t.Errorf("diff: %s", util.ObjectDiff(e, a)) + } + +} + func TestRESTDelete(t *testing.T) { _, rest := NewTestREST() eventA := testEvent("foo") diff --git a/pkg/registry/generic/etcd/etcd.go b/pkg/registry/generic/etcd/etcd.go index 8dfda348fc9..f2c02e80f60 100644 --- a/pkg/registry/generic/etcd/etcd.go +++ b/pkg/registry/generic/etcd/etcd.go @@ -103,7 +103,7 @@ func (e *Etcd) Update(ctx api.Context, id string, obj runtime.Object) error { return err } // TODO: verify that SetObj checks ResourceVersion before succeeding. - err = e.Helper.SetObj(key, obj) + err = e.Helper.SetObj(key, obj, 0 /* ttl */) return etcderr.InterpretUpdateError(err, e.EndpointName, id) } diff --git a/pkg/tools/etcd_tools.go b/pkg/tools/etcd_tools.go index 430f50ae8bb..1e5e8324231 100644 --- a/pkg/tools/etcd_tools.go +++ b/pkg/tools/etcd_tools.go @@ -281,22 +281,22 @@ func (h *EtcdHelper) Delete(key string, recursive bool) error { return err } -// SetObj marshals obj via json, and stores under key. Will do an -// atomic update if obj's ResourceVersion field is set. -func (h *EtcdHelper) SetObj(key string, obj runtime.Object) error { +// SetObj marshals obj via json, and stores under key. Will do an atomic update if obj's ResourceVersion +// field is set. 'ttl' is time-to-live in seconds, and 0 means forever. +func (h *EtcdHelper) SetObj(key string, obj runtime.Object, ttl uint64) error { data, err := h.Codec.Encode(obj) if err != nil { return err } if h.ResourceVersioner != nil { if version, err := h.ResourceVersioner.ResourceVersion(obj); err == nil && version != 0 { - _, err = h.Client.CompareAndSwap(key, string(data), 0, "", version) + _, err = h.Client.CompareAndSwap(key, string(data), ttl, "", version) return err // err is shadowed! } } // Create will fail if a key already exists. - _, err = h.Client.Create(key, string(data), 0) + _, err = h.Client.Create(key, string(data), ttl) return err } diff --git a/pkg/tools/etcd_tools_test.go b/pkg/tools/etcd_tools_test.go index 0961aa60bf0..fadcd062024 100644 --- a/pkg/tools/etcd_tools_test.go +++ b/pkg/tools/etcd_tools_test.go @@ -375,7 +375,7 @@ func TestSetObj(t *testing.T) { obj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} fakeClient := NewFakeEtcdClient(t) helper := EtcdHelper{fakeClient, testapi.Codec(), versioner} - err := helper.SetObj("/some/key", obj) + err := helper.SetObj("/some/key", obj, 5) if err != nil { t.Errorf("Unexpected error %#v", err) } @@ -388,6 +388,10 @@ func TestSetObj(t *testing.T) { if expect != got { t.Errorf("Wanted %v, got %v", expect, got) } + if e, a := uint64(5), fakeClient.LastSetTTL; e != a { + t.Errorf("Wanted %v, got %v", e, a) + } + } func TestSetObjWithVersion(t *testing.T) { @@ -404,7 +408,7 @@ func TestSetObjWithVersion(t *testing.T) { } helper := EtcdHelper{fakeClient, testapi.Codec(), versioner} - err := helper.SetObj("/some/key", obj) + err := helper.SetObj("/some/key", obj, 7) if err != nil { t.Fatalf("Unexpected error %#v", err) } @@ -417,13 +421,16 @@ func TestSetObjWithVersion(t *testing.T) { if expect != got { t.Errorf("Wanted %v, got %v", expect, got) } + if e, a := uint64(7), fakeClient.LastSetTTL; e != a { + t.Errorf("Wanted %v, got %v", e, a) + } } func TestSetObjWithoutResourceVersioner(t *testing.T) { obj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} fakeClient := NewFakeEtcdClient(t) helper := EtcdHelper{fakeClient, testapi.Codec(), nil} - err := helper.SetObj("/some/key", obj) + err := helper.SetObj("/some/key", obj, 3) if err != nil { t.Errorf("Unexpected error %#v", err) } @@ -436,6 +443,9 @@ func TestSetObjWithoutResourceVersioner(t *testing.T) { if expect != got { t.Errorf("Wanted %v, got %v", expect, got) } + if e, a := uint64(3), fakeClient.LastSetTTL; e != a { + t.Errorf("Wanted %v, got %v", e, a) + } } func TestAtomicUpdate(t *testing.T) { diff --git a/pkg/tools/fake_etcd_client.go b/pkg/tools/fake_etcd_client.go index 5250187cc97..fade54a45b7 100644 --- a/pkg/tools/fake_etcd_client.go +++ b/pkg/tools/fake_etcd_client.go @@ -173,6 +173,7 @@ func (f *FakeEtcdClient) setLocked(key, value string, ttl uint64) (*etcd.Respons Value: value, CreatedIndex: createdIndex, ModifiedIndex: i, + TTL: int64(ttl), }, }, } diff --git a/test/integration/auth_test.go b/test/integration/auth_test.go index d4fde99808b..397c2c4b020 100644 --- a/test/integration/auth_test.go +++ b/test/integration/auth_test.go @@ -24,12 +24,14 @@ package integration import ( "bytes" + "encoding/json" "errors" "fmt" "io/ioutil" "net/http" "net/http/httptest" "os" + "strings" "testing" "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" @@ -73,7 +75,7 @@ var aPod string = ` "id": "a", "containers": [{ "name": "foo", "image": "bar/foo", }] } - }, + }%s } ` var aRC string = ` @@ -97,7 +99,7 @@ var aRC string = ` }, "labels": {"name": "a"} }}, - "labels": {"name": "a"} + "labels": {"name": "a"}%s } ` var aService string = ` @@ -108,7 +110,7 @@ var aService string = ` "port": 8000, "portalIP": "10.0.0.100", "labels": { "name": "a" }, - "selector": { "name": "a" } + "selector": { "name": "a" }%s } ` var aMinion string = ` @@ -116,7 +118,7 @@ var aMinion string = ` "kind": "Minion", "apiVersion": "v1beta1", "id": "a", - "hostIP": "10.10.10.10", + "hostIP": "10.10.10.10"%s } ` @@ -131,7 +133,7 @@ var aEvent string = ` "name": "a", "namespace": "default", "apiVersion": "v1beta1", - } + }%s } ` @@ -141,7 +143,7 @@ var aBinding string = ` "apiVersion": "v1beta1", "id": "a", "host": "10.10.10.10", - "podID": "a" + "podID": "a"%s } ` @@ -150,7 +152,7 @@ var aEndpoints string = ` "kind": "Endpoints", "apiVersion": "v1beta1", "id": "a", - "endpoints": ["10.10.1.1:1909"], + "endpoints": ["10.10.1.1:1909"]%s } ` @@ -183,7 +185,7 @@ func getTestRequests() []struct { // Normal methods on pods {"GET", "/api/v1beta1/pods", "", code200}, {"POST", "/api/v1beta1/pods" + timeoutFlag, aPod, code200}, - {"PUT", "/api/v1beta1/pods/a" + timeoutFlag, aPod, code500}, // See #2114 about why 500 + {"PUT", "/api/v1beta1/pods/a" + timeoutFlag, aPod, code200}, {"GET", "/api/v1beta1/pods", "", code200}, {"GET", "/api/v1beta1/pods/a", "", code200}, {"DELETE", "/api/v1beta1/pods/a" + timeoutFlag, "", code200}, @@ -203,7 +205,7 @@ func getTestRequests() []struct { // Normal methods on services {"GET", "/api/v1beta1/services", "", code200}, {"POST", "/api/v1beta1/services" + timeoutFlag, aService, code200}, - {"PUT", "/api/v1beta1/services/a" + timeoutFlag, aService, code409}, // See #2115 about why 409 + {"PUT", "/api/v1beta1/services/a" + timeoutFlag, aService, code200}, {"GET", "/api/v1beta1/services", "", code200}, {"GET", "/api/v1beta1/services/a", "", code200}, {"DELETE", "/api/v1beta1/services/a" + timeoutFlag, "", code200}, @@ -211,7 +213,7 @@ func getTestRequests() []struct { // Normal methods on replicationControllers {"GET", "/api/v1beta1/replicationControllers", "", code200}, {"POST", "/api/v1beta1/replicationControllers" + timeoutFlag, aRC, code200}, - {"PUT", "/api/v1beta1/replicationControllers/a" + timeoutFlag, aRC, code409}, // See #2115 about why 409 + {"PUT", "/api/v1beta1/replicationControllers/a" + timeoutFlag, aRC, code200}, {"GET", "/api/v1beta1/replicationControllers", "", code200}, {"GET", "/api/v1beta1/replicationControllers/a", "", code200}, {"DELETE", "/api/v1beta1/replicationControllers/a" + timeoutFlag, "", code200}, @@ -235,7 +237,7 @@ func getTestRequests() []struct { // Normal methods on events {"GET", "/api/v1beta1/events", "", code200}, {"POST", "/api/v1beta1/events" + timeoutFlag, aEvent, code200}, - {"PUT", "/api/v1beta1/events/a" + timeoutFlag, aEvent, code405}, + {"PUT", "/api/v1beta1/events/a" + timeoutFlag, aEvent, code200}, {"GET", "/api/v1beta1/events", "", code200}, {"GET", "/api/v1beta1/events", "", code200}, {"GET", "/api/v1beta1/events/a", "", code200}, @@ -308,10 +310,22 @@ func TestAuthModeAlwaysAllow(t *testing.T) { }) transport := http.DefaultTransport + previousResourceVersion := make(map[string]float64) for _, r := range getTestRequests() { t.Logf("case %v", r) - bodyBytes := bytes.NewReader([]byte(r.body)) + var bodyStr string + if r.body != "" { + bodyStr = fmt.Sprintf(r.body, "") + if r.verb == "PUT" && r.body != "" { + // For update operations, insert previous resource version + if resVersion := previousResourceVersion[getPreviousResourceVersionKey(r.URL, "")]; resVersion != 0 { + resourceVersionJson := fmt.Sprintf(",\r\n\"resourceVersion\": %v", resVersion) + bodyStr = fmt.Sprintf(r.body, resourceVersionJson) + } + } + } + bodyBytes := bytes.NewReader([]byte(bodyStr)) req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes) if err != nil { t.Fatalf("unexpected error: %v", err) @@ -322,15 +336,50 @@ func TestAuthModeAlwaysAllow(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } + b, _ := ioutil.ReadAll(resp.Body) if _, ok := r.statusCodes[resp.StatusCode]; !ok { t.Errorf("Expected status one of %v, but got %v", r.statusCodes, resp.StatusCode) - b, _ := ioutil.ReadAll(resp.Body) t.Errorf("Body: %v", string(b)) + } else { + if r.verb == "POST" { + // For successful create operations, extract resourceVersion + id, currentResourceVersion, err := parseResourceVersion(b) + if err == nil { + key := getPreviousResourceVersionKey(r.URL, id) + previousResourceVersion[key] = currentResourceVersion + } + } } }() } } +func parseResourceVersion(response []byte) (string, float64, error) { + var resultBodyMap map[string]interface{} + err := json.Unmarshal(response, &resultBodyMap) + if err != nil { + return "", 0, fmt.Errorf("unexpected error unmarshaling resultBody: %v", err) + } + id, ok := resultBodyMap["id"].(string) + if !ok { + return "", 0, fmt.Errorf("unexpected error, id not found in JSON response: %v", string(response)) + } + resourceVersion, ok := resultBodyMap["resourceVersion"].(float64) + if !ok { + return "", 0, fmt.Errorf("unexpected error, resourceVersion not found in JSON response: %v", string(response)) + } + return id, resourceVersion, nil +} + +func getPreviousResourceVersionKey(url, id string) string { + baseUrl := strings.Split(url, "?")[0] + key := baseUrl + if id != "" { + key = fmt.Sprintf("%s/%v", baseUrl, id) + } + return key +} + func TestAuthModeAlwaysDeny(t *testing.T) { deleteAllEtcdKeys() @@ -426,12 +475,24 @@ func TestAliceNotForbiddenOrUnauthorized(t *testing.T) { AdmissionControl: admit.NewAlwaysAdmit(), }) + previousResourceVersion := make(map[string]float64) transport := http.DefaultTransport for _, r := range getTestRequests() { token := AliceToken t.Logf("case %v", r) - bodyBytes := bytes.NewReader([]byte(r.body)) + var bodyStr string + if r.body != "" { + bodyStr = fmt.Sprintf(r.body, "") + if r.verb == "PUT" && r.body != "" { + // For update operations, insert previous resource version + if resVersion := previousResourceVersion[getPreviousResourceVersionKey(r.URL, "")]; resVersion != 0 { + resourceVersionJson := fmt.Sprintf(",\r\n\"resourceVersion\": %v", resVersion) + bodyStr = fmt.Sprintf(r.body, resourceVersionJson) + } + } + } + bodyBytes := bytes.NewReader([]byte(bodyStr)) req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes) if err != nil { t.Fatalf("unexpected error: %v", err) @@ -444,11 +505,21 @@ func TestAliceNotForbiddenOrUnauthorized(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } + b, _ := ioutil.ReadAll(resp.Body) if _, ok := r.statusCodes[resp.StatusCode]; !ok { t.Errorf("Expected status one of %v, but got %v", r.statusCodes, resp.StatusCode) - b, _ := ioutil.ReadAll(resp.Body) t.Errorf("Body: %v", string(b)) + } else { + if r.verb == "POST" { + // For successful create operations, extract resourceVersion + id, currentResourceVersion, err := parseResourceVersion(b) + if err == nil { + key := getPreviousResourceVersionKey(r.URL, id) + previousResourceVersion[key] = currentResourceVersion + } + } } + }() } } @@ -628,6 +699,7 @@ func TestNamespaceAuthorization(t *testing.T) { AdmissionControl: admit.NewAlwaysAdmit(), }) + previousResourceVersion := make(map[string]float64) transport := http.DefaultTransport requests := []struct { @@ -656,7 +728,18 @@ func TestNamespaceAuthorization(t *testing.T) { for _, r := range requests { token := BobToken t.Logf("case %v", r) - bodyBytes := bytes.NewReader([]byte(r.body)) + var bodyStr string + if r.body != "" { + bodyStr = fmt.Sprintf(r.body, "") + if r.verb == "PUT" && r.body != "" { + // For update operations, insert previous resource version + if resVersion := previousResourceVersion[getPreviousResourceVersionKey(r.URL, "")]; resVersion != 0 { + resourceVersionJson := fmt.Sprintf(",\r\n\"resourceVersion\": %v", resVersion) + bodyStr = fmt.Sprintf(r.body, resourceVersionJson) + } + } + } + bodyBytes := bytes.NewReader([]byte(bodyStr)) req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes) if err != nil { t.Fatalf("unexpected error: %v", err) @@ -668,11 +751,21 @@ func TestNamespaceAuthorization(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } + b, _ := ioutil.ReadAll(resp.Body) if _, ok := r.statusCodes[resp.StatusCode]; !ok { t.Errorf("Expected status one of %v, but got %v", r.statusCodes, resp.StatusCode) - b, _ := ioutil.ReadAll(resp.Body) t.Errorf("Body: %v", string(b)) + } else { + if r.verb == "POST" { + // For successful create operations, extract resourceVersion + id, currentResourceVersion, err := parseResourceVersion(b) + if err == nil { + key := getPreviousResourceVersionKey(r.URL, id) + previousResourceVersion[key] = currentResourceVersion + } + } } + }() } } @@ -713,6 +806,7 @@ func TestKindAuthorization(t *testing.T) { AdmissionControl: admit.NewAlwaysAdmit(), }) + previousResourceVersion := make(map[string]float64) transport := http.DefaultTransport requests := []struct { @@ -735,7 +829,18 @@ func TestKindAuthorization(t *testing.T) { for _, r := range requests { token := BobToken t.Logf("case %v", r) - bodyBytes := bytes.NewReader([]byte(r.body)) + var bodyStr string + if r.body != "" { + bodyStr = fmt.Sprintf(r.body, "") + if r.verb == "PUT" && r.body != "" { + // For update operations, insert previous resource version + if resVersion := previousResourceVersion[getPreviousResourceVersionKey(r.URL, "")]; resVersion != 0 { + resourceVersionJson := fmt.Sprintf(",\r\n\"resourceVersion\": %v", resVersion) + bodyStr = fmt.Sprintf(r.body, resourceVersionJson) + } + } + } + bodyBytes := bytes.NewReader([]byte(bodyStr)) req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes) if err != nil { t.Fatalf("unexpected error: %v", err) @@ -747,11 +852,21 @@ func TestKindAuthorization(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } + b, _ := ioutil.ReadAll(resp.Body) if _, ok := r.statusCodes[resp.StatusCode]; !ok { t.Errorf("Expected status one of %v, but got %v", r.statusCodes, resp.StatusCode) - b, _ := ioutil.ReadAll(resp.Body) t.Errorf("Body: %v", string(b)) + } else { + if r.verb == "POST" { + // For successful create operations, extract resourceVersion + id, currentResourceVersion, err := parseResourceVersion(b) + if err == nil { + key := getPreviousResourceVersionKey(r.URL, id) + previousResourceVersion[key] = currentResourceVersion + } + } } + } } } diff --git a/test/integration/etcd_tools_test.go b/test/integration/etcd_tools_test.go index 0a621fe092f..66c009cc6b3 100644 --- a/test/integration/etcd_tools_test.go +++ b/test/integration/etcd_tools_test.go @@ -60,7 +60,7 @@ func TestSetObj(t *testing.T) { helper := tools.EtcdHelper{Client: client, Codec: stringCodec{}} withEtcdKey(func(key string) { fakeObject := fakeAPIObject("object") - if err := helper.SetObj(key, &fakeObject); err != nil { + if err := helper.SetObj(key, &fakeObject, 0 /* ttl */); err != nil { t.Fatalf("unexpected error: %v", err) } resp, err := client.Get(key, false, false) From 7bb62cbfb3c6fc4d6979948d1730ba7cf9ab4726 Mon Sep 17 00:00:00 2001 From: Filipe Brandenburger Date: Thu, 5 Feb 2015 00:00:28 -0800 Subject: [PATCH 24/50] Make e2e shell tests work standalone Simply incorporate some of the boilerplate from hack/e2e.go into the scripts in hack/e2e-suite. Use environment variables with default values to allow overrides in kubectl command line and to use a versioned package root. Tested: - Ran `go run hack/e2e.go -test` on a test cluster. - Ran the test cases individually. - Ran hack/e2e-suite/goe2e.sh -t Pods to confirm it takes arguments. - Also fixed cluster/test-network.sh (which should be more and more irrelevant.) --- cluster/test-network.sh | 4 +--- hack/e2e-suite/certs.sh | 11 ++++++++++- hack/e2e-suite/goe2e.sh | 21 +++++++++++++++++---- hack/e2e-suite/guestbook.sh | 11 ++++++++++- hack/e2e-suite/liveness.sh | 11 ++++++++++- hack/e2e-suite/monitoring.sh | 11 ++++++++++- hack/e2e-suite/pd.sh | 11 ++++++++++- hack/e2e-suite/services.sh | 11 ++++++++++- hack/e2e-suite/update.sh | 12 ++++++++++-- hack/e2e.go | 3 +++ 10 files changed, 91 insertions(+), 15 deletions(-) diff --git a/cluster/test-network.sh b/cluster/test-network.sh index 0480e137eb6..df456caead7 100755 --- a/cluster/test-network.sh +++ b/cluster/test-network.sh @@ -19,7 +19,5 @@ set -o nounset set -o pipefail KUBE_ROOT=$(dirname "${BASH_SOURCE}")/.. -source "${KUBE_ROOT}/cluster/kube-env.sh" -source "${KUBE_ROOT}/cluster/${KUBERNETES_PROVIDER}/util.sh" -${KUBE_ROOT}/hack/e2e-suite/goe2e.sh -tTestNetwork +exec "${KUBE_ROOT}/hack/e2e-suite/goe2e.sh" -t TestNetwork diff --git a/hack/e2e-suite/certs.sh b/hack/e2e-suite/certs.sh index 782e4ff7099..fb1574be20d 100755 --- a/hack/e2e-suite/certs.sh +++ b/hack/e2e-suite/certs.sh @@ -22,8 +22,17 @@ set -o nounset set -o pipefail KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../.. + +: ${KUBE_VERSION_ROOT:=${KUBE_ROOT}} +: ${KUBECTL:="${KUBE_VERSION_ROOT}/cluster/kubectl.sh"} +: ${KUBE_CONFIG_FILE:="config-test.sh"} + +export KUBECTL KUBE_CONFIG_FILE + source "${KUBE_ROOT}/cluster/kube-env.sh" -source "${KUBE_ROOT}/cluster/${KUBERNETES_PROVIDER}/util.sh" +source "${KUBE_VERSION_ROOT}/cluster/${KUBERNETES_PROVIDER}/util.sh" + +prepare-e2e if [[ "${KUBERNETES_PROVIDER}" != "gce" ]] && [[ "${KUBERNETES_PROVIDER}" != "gke" ]]; then echo "WARNING: Skipping certs.sh for cloud provider: ${KUBERNETES_PROVIDER}." diff --git a/hack/e2e-suite/goe2e.sh b/hack/e2e-suite/goe2e.sh index b2938637aa7..daffe8830c1 100755 --- a/hack/e2e-suite/goe2e.sh +++ b/hack/e2e-suite/goe2e.sh @@ -14,11 +14,24 @@ # See the License for the specific language governing permissions and # limitations under the License. -KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../.. -source "${KUBE_ROOT}/cluster/kube-env.sh" -source "${KUBE_ROOT}/cluster/$KUBERNETES_PROVIDER/util.sh" +set -o errexit +set -o nounset +set -o pipefail -detect-master > /dev/null +KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../.. + +: ${KUBE_VERSION_ROOT:=${KUBE_ROOT}} +: ${KUBECTL:="${KUBE_VERSION_ROOT}/cluster/kubectl.sh"} +: ${KUBE_CONFIG_FILE:="config-test.sh"} + +export KUBECTL KUBE_CONFIG_FILE + +source "${KUBE_ROOT}/cluster/kube-env.sh" +source "${KUBE_VERSION_ROOT}/cluster/${KUBERNETES_PROVIDER}/util.sh" + +prepare-e2e + +detect-master >/dev/null # Detect the OS name/arch so that we can find our binary case "$(uname -s)" in diff --git a/hack/e2e-suite/guestbook.sh b/hack/e2e-suite/guestbook.sh index 8b348b36fe2..274194fc373 100755 --- a/hack/e2e-suite/guestbook.sh +++ b/hack/e2e-suite/guestbook.sh @@ -23,8 +23,17 @@ set -o nounset set -o pipefail KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../.. + +: ${KUBE_VERSION_ROOT:=${KUBE_ROOT}} +: ${KUBECTL:="${KUBE_VERSION_ROOT}/cluster/kubectl.sh"} +: ${KUBE_CONFIG_FILE:="config-test.sh"} + +export KUBECTL KUBE_CONFIG_FILE + source "${KUBE_ROOT}/cluster/kube-env.sh" -source "${KUBE_ROOT}/cluster/$KUBERNETES_PROVIDER/util.sh" +source "${KUBE_VERSION_ROOT}/cluster/${KUBERNETES_PROVIDER}/util.sh" + +prepare-e2e GUESTBOOK="${KUBE_ROOT}/examples/guestbook" diff --git a/hack/e2e-suite/liveness.sh b/hack/e2e-suite/liveness.sh index 7178430289f..cb78dfe6364 100755 --- a/hack/e2e-suite/liveness.sh +++ b/hack/e2e-suite/liveness.sh @@ -22,8 +22,17 @@ set -o nounset set -o pipefail KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../.. + +: ${KUBE_VERSION_ROOT:=${KUBE_ROOT}} +: ${KUBECTL:="${KUBE_VERSION_ROOT}/cluster/kubectl.sh"} +: ${KUBE_CONFIG_FILE:="config-test.sh"} + +export KUBECTL KUBE_CONFIG_FILE + source "${KUBE_ROOT}/cluster/kube-env.sh" -source "${KUBE_ROOT}/cluster/$KUBERNETES_PROVIDER/util.sh" +source "${KUBE_VERSION_ROOT}/cluster/${KUBERNETES_PROVIDER}/util.sh" + +prepare-e2e liveness_tests="http exec" if [[ ${KUBERNETES_PROVIDER} == "gke" ]]; then diff --git a/hack/e2e-suite/monitoring.sh b/hack/e2e-suite/monitoring.sh index 78df2d8a67d..d999af4fda3 100755 --- a/hack/e2e-suite/monitoring.sh +++ b/hack/e2e-suite/monitoring.sh @@ -23,8 +23,17 @@ set -o nounset set -o pipefail KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../.. + +: ${KUBE_VERSION_ROOT:=${KUBE_ROOT}} +: ${KUBECTL:="${KUBE_VERSION_ROOT}/cluster/kubectl.sh"} +: ${KUBE_CONFIG_FILE:="config-test.sh"} + +export KUBECTL KUBE_CONFIG_FILE + source "${KUBE_ROOT}/cluster/kube-env.sh" -source "${KUBE_ROOT}/cluster/$KUBERNETES_PROVIDER/util.sh" +source "${KUBE_VERSION_ROOT}/cluster/${KUBERNETES_PROVIDER}/util.sh" + +prepare-e2e MONITORING="${KUBE_ROOT}/cluster/addons/cluster-monitoring" KUBECTL="${KUBE_ROOT}/cluster/kubectl.sh" diff --git a/hack/e2e-suite/pd.sh b/hack/e2e-suite/pd.sh index 673e69d2fe4..2910023021a 100755 --- a/hack/e2e-suite/pd.sh +++ b/hack/e2e-suite/pd.sh @@ -22,8 +22,17 @@ set -o nounset set -o pipefail KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../.. + +: ${KUBE_VERSION_ROOT:=${KUBE_ROOT}} +: ${KUBECTL:="${KUBE_VERSION_ROOT}/cluster/kubectl.sh"} +: ${KUBE_CONFIG_FILE:="config-test.sh"} + +export KUBECTL KUBE_CONFIG_FILE + source "${KUBE_ROOT}/cluster/kube-env.sh" -source "${KUBE_ROOT}/cluster/$KUBERNETES_PROVIDER/util.sh" +source "${KUBE_VERSION_ROOT}/cluster/${KUBERNETES_PROVIDER}/util.sh" + +prepare-e2e if [[ "$KUBERNETES_PROVIDER" != "gce" ]] && [[ "$KUBERNETES_PROVIDER" != "gke" ]]; then echo "WARNING: Skipping pd.sh for cloud provider: ${KUBERNETES_PROVIDER}." diff --git a/hack/e2e-suite/services.sh b/hack/e2e-suite/services.sh index ed8926e51d3..a8b4ec9d031 100755 --- a/hack/e2e-suite/services.sh +++ b/hack/e2e-suite/services.sh @@ -21,8 +21,17 @@ set -o nounset set -o pipefail KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../.. + +: ${KUBE_VERSION_ROOT:=${KUBE_ROOT}} +: ${KUBECTL:="${KUBE_VERSION_ROOT}/cluster/kubectl.sh"} +: ${KUBE_CONFIG_FILE:="config-test.sh"} + +export KUBECTL KUBE_CONFIG_FILE + source "${KUBE_ROOT}/cluster/kube-env.sh" -source "${KUBE_ROOT}/cluster/${KUBERNETES_PROVIDER}/util.sh" +source "${KUBE_VERSION_ROOT}/cluster/${KUBERNETES_PROVIDER}/util.sh" + +prepare-e2e if [[ "$KUBERNETES_PROVIDER" == "vagrant" ]]; then echo "WARNING: Skipping services.sh for ${KUBERNETES_PROVIDER}. See https://github.com/GoogleCloudPlatform/kubernetes/issues/3655" diff --git a/hack/e2e-suite/update.sh b/hack/e2e-suite/update.sh index 6ddc1ab32e8..c147492e48c 100755 --- a/hack/e2e-suite/update.sh +++ b/hack/e2e-suite/update.sh @@ -21,9 +21,17 @@ set -o nounset set -o pipefail KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../.. -source "${KUBE_ROOT}/cluster/kube-env.sh" -source "${KUBE_ROOT}/cluster/$KUBERNETES_PROVIDER/util.sh" +: ${KUBE_VERSION_ROOT:=${KUBE_ROOT}} +: ${KUBECTL:="${KUBE_VERSION_ROOT}/cluster/kubectl.sh"} +: ${KUBE_CONFIG_FILE:="config-test.sh"} + +export KUBECTL KUBE_CONFIG_FILE + +source "${KUBE_ROOT}/cluster/kube-env.sh" +source "${KUBE_VERSION_ROOT}/cluster/${KUBERNETES_PROVIDER}/util.sh" + +prepare-e2e CONTROLLER_NAME=update-demo diff --git a/hack/e2e.go b/hack/e2e.go index b5d8da8a8d3..0ca85d26f67 100644 --- a/hack/e2e.go +++ b/hack/e2e.go @@ -137,9 +137,12 @@ func main() { log.Fatalf("Error preparing a binary of version %s: %s. Aborting.", *version, err) } else { versionRoot = newVersionRoot + os.Setenv("KUBE_VERSION_ROOT", newVersionRoot) } } + os.Setenv("KUBECTL", versionRoot+`/cluster/kubectl.sh`+kubectlArgs()) + if *pushup { if IsUp() { log.Printf("e2e cluster is up, pushing.") From 8ded7be5fb270145bf9caa3a5afb9fab345cbcbd Mon Sep 17 00:00:00 2001 From: Pierre du Plessis Date: Thu, 5 Feb 2015 15:10:32 +0200 Subject: [PATCH 25/50] Typo --- examples/guestbook/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/guestbook/README.md b/examples/guestbook/README.md index 61fb160a994..177f5b3632b 100644 --- a/examples/guestbook/README.md +++ b/examples/guestbook/README.md @@ -6,7 +6,7 @@ The example combines a web frontend, a redis master for storage and a replicated ### Step Zero: Prerequisites -This example assumes that have a basic understanding of kubernetes services and that you have forked the repository and [turned up a Kubernetes cluster](https://github.com/GoogleCloudPlatform/kubernetes#contents): +This example assumes that you have a basic understanding of kubernetes services and that you have forked the repository and [turned up a Kubernetes cluster](https://github.com/GoogleCloudPlatform/kubernetes#contents): ```shell $ cd kubernetes From e8c33b7916253c98a0b0a3b947593e4632ca1f1c Mon Sep 17 00:00:00 2001 From: George Kuan Date: Wed, 28 Jan 2015 20:35:49 -0800 Subject: [PATCH 26/50] Add timeouts to HealthChecks and retry checks Fixes issue #3532. Added timeouts for HTTP and TCP checks and enabled kubelet/probe to kubelet#maxRetries times before declaring Failure. Added Probe.TimeoutSecs to API Probe variants now check container.LivenessProbe.TimeoutSeconds Also added a test for timeouts in http_test.go. --- pkg/api/types.go | 2 ++ pkg/api/v1beta1/conversion.go | 2 ++ pkg/api/v1beta1/types.go | 2 ++ pkg/api/v1beta2/conversion.go | 2 ++ pkg/api/v1beta2/types.go | 2 ++ pkg/api/v1beta3/types.go | 2 ++ pkg/kubelet/kubelet.go | 11 +++++++++-- pkg/kubelet/probe.go | 14 ++++++++++++-- pkg/probe/http/http.go | 10 ++++++---- pkg/probe/http/http_test.go | 22 ++++++++++++++-------- pkg/probe/tcp/tcp.go | 9 +++++---- pkg/probe/tcp/tcp_test.go | 3 ++- 12 files changed, 60 insertions(+), 21 deletions(-) diff --git a/pkg/api/types.go b/pkg/api/types.go index a9179a16b23..c2c2db28efb 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -279,6 +279,8 @@ type Probe struct { Handler `json:",inline"` // Length of time before health checking is activated. In seconds. InitialDelaySeconds int64 `json:"initialDelaySeconds,omitempty"` + // Length of time before health checking times out. In seconds. + TimeoutSeconds int64 `json:"timeoutSeconds,omitempty"` } // PullPolicy describes a policy for if/when to pull a container image diff --git a/pkg/api/v1beta1/conversion.go b/pkg/api/v1beta1/conversion.go index a205d054fec..4058a01cf54 100644 --- a/pkg/api/v1beta1/conversion.go +++ b/pkg/api/v1beta1/conversion.go @@ -911,6 +911,7 @@ func init() { return err } out.InitialDelaySeconds = in.InitialDelaySeconds + out.TimeoutSeconds = in.TimeoutSeconds return nil }, func(in *LivenessProbe, out *newer.Probe, s conversion.Scope) error { @@ -924,6 +925,7 @@ func init() { return err } out.InitialDelaySeconds = in.InitialDelaySeconds + out.TimeoutSeconds = in.TimeoutSeconds return nil }, ) diff --git a/pkg/api/v1beta1/types.go b/pkg/api/v1beta1/types.go index 6940734702a..6b0e1c59d3f 100644 --- a/pkg/api/v1beta1/types.go +++ b/pkg/api/v1beta1/types.go @@ -227,6 +227,8 @@ type LivenessProbe struct { Exec *ExecAction `json:"exec,omitempty" description:"parameters for exec-based liveness probe"` // Length of time before health checking is activated. In seconds. InitialDelaySeconds int64 `json:"initialDelaySeconds,omitempty" description:"number of seconds after the container has started before liveness probes are initiated"` + // Length of time before health checking times out. In seconds. + TimeoutSeconds int64 `json:"timeoutSeconds,omitempty" description:"number of seconds after which liveness probes timeout; defaults to 1 second"` } // PullPolicy describes a policy for if/when to pull a container image diff --git a/pkg/api/v1beta2/conversion.go b/pkg/api/v1beta2/conversion.go index 4ae43951a2c..336cf64fe20 100644 --- a/pkg/api/v1beta2/conversion.go +++ b/pkg/api/v1beta2/conversion.go @@ -824,6 +824,7 @@ func init() { return err } out.InitialDelaySeconds = in.InitialDelaySeconds + out.TimeoutSeconds = in.TimeoutSeconds return nil }, func(in *LivenessProbe, out *newer.Probe, s conversion.Scope) error { @@ -837,6 +838,7 @@ func init() { return err } out.InitialDelaySeconds = in.InitialDelaySeconds + out.TimeoutSeconds = in.TimeoutSeconds return nil }, ) diff --git a/pkg/api/v1beta2/types.go b/pkg/api/v1beta2/types.go index 052b4ee777e..0d18fe914ee 100644 --- a/pkg/api/v1beta2/types.go +++ b/pkg/api/v1beta2/types.go @@ -191,6 +191,8 @@ type LivenessProbe struct { Exec *ExecAction `json:"exec,omitempty" description:"parameters for exec-based liveness probe"` // Length of time before health checking is activated. In seconds. InitialDelaySeconds int64 `json:"initialDelaySeconds,omitempty" description:"number of seconds after the container has started before liveness probes are initiated"` + // Length of time before health checking times out. In seconds. + TimeoutSeconds int64 `json:"timeoutSeconds,omitempty" description:"number of seconds after which liveness probes timeout; defaults to 1 second"` } // PullPolicy describes a policy for if/when to pull a container image diff --git a/pkg/api/v1beta3/types.go b/pkg/api/v1beta3/types.go index 26c7a07478d..2c628534dda 100644 --- a/pkg/api/v1beta3/types.go +++ b/pkg/api/v1beta3/types.go @@ -297,6 +297,8 @@ type Probe struct { Handler `json:",inline"` // Length of time before health checking is activated. In seconds. InitialDelaySeconds int64 `json:"initialDelaySeconds,omitempty"` + // Length of time before health checking times out. In seconds. + TimeoutSeconds int64 `json:"timeoutSeconds,omitempty"` } // PullPolicy describes a policy for if/when to pull a container image diff --git a/pkg/kubelet/kubelet.go b/pkg/kubelet/kubelet.go index a2a1f113d4b..d0b284bee88 100644 --- a/pkg/kubelet/kubelet.go +++ b/pkg/kubelet/kubelet.go @@ -55,6 +55,7 @@ const defaultChanSize = 1024 const minShares = 2 const sharesPerCPU = 1024 const milliCPUToCPU = 1000 +const maxRetries int = 3 // SyncHandler is an interface implemented by Kubelet, for testability type SyncHandler interface { @@ -1417,7 +1418,7 @@ func (kl *Kubelet) GetPodStatus(podFullName string, uid types.UID) (api.PodStatu return podStatus, err } -func (kl *Kubelet) probeLiveness(podFullName string, podUID types.UID, status api.PodStatus, container api.Container, dockerContainer *docker.APIContainers) (probe.Status, error) { +func (kl *Kubelet) probeLiveness(podFullName string, podUID types.UID, status api.PodStatus, container api.Container, dockerContainer *docker.APIContainers) (healthStatus probe.Status, err error) { // Give the container 60 seconds to start up. if container.LivenessProbe == nil { return probe.Success, nil @@ -1425,7 +1426,13 @@ func (kl *Kubelet) probeLiveness(podFullName string, podUID types.UID, status ap if time.Now().Unix()-dockerContainer.Created < container.LivenessProbe.InitialDelaySeconds { return probe.Success, nil } - return kl.probeContainer(container.LivenessProbe, podFullName, podUID, status, container) + for i := 0; i < maxRetries; i++ { + healthStatus, err = kl.probeContainer(container.LivenessProbe, podFullName, podUID, status, container) + if healthStatus == probe.Success { + return + } + } + return healthStatus, err } // Returns logs of current machine. diff --git a/pkg/kubelet/probe.go b/pkg/kubelet/probe.go index 8d107872d70..72bc972c5f4 100644 --- a/pkg/kubelet/probe.go +++ b/pkg/kubelet/probe.go @@ -19,6 +19,7 @@ package kubelet import ( "fmt" "strconv" + "time" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/probe" @@ -39,6 +40,14 @@ var ( ) func (kl *Kubelet) probeContainer(p *api.Probe, podFullName string, podUID types.UID, status api.PodStatus, container api.Container) (probe.Status, error) { + var timeout time.Duration + secs := container.LivenessProbe.TimeoutSeconds + if secs > 0 { + timeout = time.Duration(secs) * time.Second + } else { + timeout = 1 * time.Second + } + if p.Exec != nil { return execprober.Probe(kl.newExecInContainer(podFullName, podUID, container)) } @@ -47,14 +56,15 @@ func (kl *Kubelet) probeContainer(p *api.Probe, podFullName string, podUID types if err != nil { return probe.Unknown, err } - return httprober.Probe(extractGetParams(p.HTTPGet, status, port)) + host, port, path := extractGetParams(p.HTTPGet, status, port) + return httprober.Probe(host, port, path, timeout) } if p.TCPSocket != nil { port, err := extractPort(p.TCPSocket.Port, container) if err != nil { return probe.Unknown, err } - return tcprober.Probe(status.PodIP, port) + return tcprober.Probe(status.PodIP, port, timeout) } glog.Warningf("Failed to find probe builder for %s %+v", container.Name, container.LivenessProbe) return probe.Unknown, nil diff --git a/pkg/probe/http/http.go b/pkg/probe/http/http.go index e3e7898dbc2..09f89178823 100644 --- a/pkg/probe/http/http.go +++ b/pkg/probe/http/http.go @@ -21,6 +21,7 @@ import ( "net/http" "net/url" "strconv" + "time" "github.com/GoogleCloudPlatform/kubernetes/pkg/probe" @@ -28,16 +29,17 @@ import ( ) func New() HTTPProber { - return HTTPProber{&http.Client{}} + transport := &http.Transport{} + return HTTPProber{transport} } type HTTPProber struct { - client HTTPGetInterface + transport *http.Transport } // Probe returns a ProbeRunner capable of running an http check. -func (pr *HTTPProber) Probe(host string, port int, path string) (probe.Status, error) { - return DoHTTPProbe(formatURL(host, port, path), pr.client) +func (pr *HTTPProber) Probe(host string, port int, path string, timeout time.Duration) (probe.Status, error) { + return DoHTTPProbe(formatURL(host, port, path), &http.Client{Timeout: timeout, Transport: pr.transport}) } type HTTPGetInterface interface { diff --git a/pkg/probe/http/http_test.go b/pkg/probe/http/http_test.go index fd9009f6615..be9b0ae8ce4 100644 --- a/pkg/probe/http/http_test.go +++ b/pkg/probe/http/http_test.go @@ -23,6 +23,7 @@ import ( "net/url" "strconv" "testing" + "time" "github.com/GoogleCloudPlatform/kubernetes/pkg/probe" ) @@ -46,20 +47,25 @@ func TestFormatURL(t *testing.T) { } func TestHTTPProbeChecker(t *testing.T) { + handleReq := func(s int) func(w http.ResponseWriter) { + return func(w http.ResponseWriter) { w.WriteHeader(s) } + } + prober := New() testCases := []struct { - status int - health probe.Status + handler func(w http.ResponseWriter) + health probe.Status }{ // The probe will be filled in below. This is primarily testing that an HTTP GET happens. - {http.StatusOK, probe.Success}, - {-1, probe.Failure}, + {handleReq(http.StatusOK), probe.Success}, + {handleReq(-1), probe.Failure}, + {func(w http.ResponseWriter) { time.Sleep(3 * time.Second) }, probe.Failure}, } for _, test := range testCases { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(test.status) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + test.handler(w) })) - u, err := url.Parse(ts.URL) + u, err := url.Parse(server.URL) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -71,7 +77,7 @@ func TestHTTPProbeChecker(t *testing.T) { if err != nil { t.Errorf("Unexpected error: %v", err) } - health, err := prober.Probe(host, p, "") + health, err := prober.Probe(host, p, "", 1*time.Second) if test.health == probe.Unknown && err == nil { t.Errorf("Expected error") } diff --git a/pkg/probe/tcp/tcp.go b/pkg/probe/tcp/tcp.go index a452a802c74..8232295a0b2 100644 --- a/pkg/probe/tcp/tcp.go +++ b/pkg/probe/tcp/tcp.go @@ -19,6 +19,7 @@ package tcp import ( "net" "strconv" + "time" "github.com/GoogleCloudPlatform/kubernetes/pkg/probe" @@ -31,16 +32,16 @@ func New() TCPProber { type TCPProber struct{} -func (pr TCPProber) Probe(host string, port int) (probe.Status, error) { - return DoTCPProbe(net.JoinHostPort(host, strconv.Itoa(port))) +func (pr TCPProber) Probe(host string, port int, timeout time.Duration) (probe.Status, error) { + return DoTCPProbe(net.JoinHostPort(host, strconv.Itoa(port)), timeout) } // DoTCPProbe checks that a TCP socket to the address can be opened. // If the socket can be opened, it returns Success // If the socket fails to open, it returns Failure. // This is exported because some other packages may want to do direct TCP probes. -func DoTCPProbe(addr string) (probe.Status, error) { - conn, err := net.Dial("tcp", addr) +func DoTCPProbe(addr string, timeout time.Duration) (probe.Status, error) { + conn, err := net.DialTimeout("tcp", addr, timeout) if err != nil { return probe.Failure, nil } diff --git a/pkg/probe/tcp/tcp_test.go b/pkg/probe/tcp/tcp_test.go index bd8f61fdac0..5be9125eb74 100644 --- a/pkg/probe/tcp/tcp_test.go +++ b/pkg/probe/tcp/tcp_test.go @@ -23,6 +23,7 @@ import ( "net/url" "strconv" "testing" + "time" "github.com/GoogleCloudPlatform/kubernetes/pkg/probe" ) @@ -59,7 +60,7 @@ func TestTcpHealthChecker(t *testing.T) { if !test.usePort { p = -1 } - status, err := prober.Probe(host, p) + status, err := prober.Probe(host, p, 1*time.Second) if status != test.expectedStatus { t.Errorf("expected: %v, got: %v", test.expectedStatus, status) } From de848cc3f098e042c9f037c0bed48bbbe0d35452 Mon Sep 17 00:00:00 2001 From: Robert Rati Date: Wed, 4 Feb 2015 09:21:37 -0500 Subject: [PATCH 27/50] Moved cluster_dns to create the new service test suite #4162 --- test/e2e/{cluster_dns.go => service.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/e2e/{cluster_dns.go => service.go} (100%) diff --git a/test/e2e/cluster_dns.go b/test/e2e/service.go similarity index 100% rename from test/e2e/cluster_dns.go rename to test/e2e/service.go From d44c1d5b842e3ea3b4e735752fcc5e39f798834a Mon Sep 17 00:00:00 2001 From: Robert Rati Date: Thu, 5 Feb 2015 08:50:07 -0500 Subject: [PATCH 28/50] Converted TestClusterDNS to native ginkgo syntax #4162 --- test/e2e/service.go | 235 ++++++++++++++++++++++---------------------- 1 file changed, 119 insertions(+), 116 deletions(-) diff --git a/test/e2e/service.go b/test/e2e/service.go index b64da904e7b..4880615720b 100644 --- a/test/e2e/service.go +++ b/test/e2e/service.go @@ -23,132 +23,135 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" - "github.com/golang/glog" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) -// TestClusterDNS checks that cluster DNS works. -func TestClusterDNS(c *client.Client) bool { - if testContext.provider == "vagrant" { - glog.Infof("Skipping test which is broken for vagrant (See https://github.com/GoogleCloudPlatform/kubernetes/issues/3580)") - return true - } +var _ = Describe("Services", func() { + var c *client.Client - podClient := c.Pods(api.NamespaceDefault) + BeforeEach(func() { + c = loadClientOrDie() + }) - //TODO: Wait for skyDNS + It("should provide DNS for the cluster", func() { + if testContext.provider == "vagrant" { + By("Skipping test which is broken for vagrant (See https://github.com/GoogleCloudPlatform/kubernetes/issues/3580)") + } else { + podClient := c.Pods(api.NamespaceDefault) - // All the names we need to be able to resolve. - namesToResolve := []string{ - "kubernetes-ro", - "kubernetes-ro.default", - "kubernetes-ro.default.kubernetes.local", - "google.com", - } + //TODO: Wait for skyDNS - probeCmd := "for i in `seq 1 600`; do " - for _, name := range namesToResolve { - probeCmd += fmt.Sprintf("wget -O /dev/null %s && echo OK > /results/%s;", name, name) - } - probeCmd += "sleep 1; done" - - // Run a pod which probes DNS and exposes the results by HTTP. - pod := &api.Pod{ - TypeMeta: api.TypeMeta{ - Kind: "Pod", - APIVersion: "v1beta1", - }, - ObjectMeta: api.ObjectMeta{ - Name: "dns-test-" + string(util.NewUUID()), - }, - Spec: api.PodSpec{ - Volumes: []api.Volume{ - { - Name: "results", - Source: api.VolumeSource{ - EmptyDir: &api.EmptyDir{}, - }, - }, - }, - Containers: []api.Container{ - { - Name: "webserver", - Image: "kubernetes/test-webserver", - VolumeMounts: []api.VolumeMount{ - { - Name: "results", - MountPath: "/results", - }, - }, - }, - { - Name: "pinger", - Image: "busybox", - Command: []string{"sh", "-c", probeCmd}, - VolumeMounts: []api.VolumeMount{ - { - Name: "results", - MountPath: "/results", - }, - }, - }, - }, - }, - } - _, err := podClient.Create(pod) - if err != nil { - glog.Errorf("Failed to create %s pod: %v", pod.Name, err) - return false - } - defer podClient.Delete(pod.Name) - - waitForPodRunning(c, pod.Name) - pod, err = podClient.Get(pod.Name) - if err != nil { - glog.Errorf("Failed to get pod %s: %v", pod.Name, err) - return false - } - - // Try to find results for each expected name. - var failed []string - for try := 1; try < 100; try++ { - failed = []string{} - for _, name := range namesToResolve { - _, err := c.Get(). - Prefix("proxy"). - Resource("pods"). - Namespace("default"). - Name(pod.Name). - Suffix("results", name). - Do().Raw() - if err != nil { - failed = append(failed, name) - glog.V(4).Infof("Lookup using %s for %s failed: %v", pod.Name, name, err) + // All the names we need to be able to resolve. + namesToResolve := []string{ + "kubernetes-ro", + "kubernetes-ro.default", + "kubernetes-ro.default.kubernetes.local", + "google.com", } + + probeCmd := "for i in `seq 1 600`; do " + for _, name := range namesToResolve { + probeCmd += fmt.Sprintf("wget -O /dev/null %s && echo OK > /results/%s;", name, name) + } + probeCmd += "sleep 1; done" + + // Run a pod which probes DNS and exposes the results by HTTP. + By("creating a pod to probe DNS") + pod := &api.Pod{ + TypeMeta: api.TypeMeta{ + Kind: "Pod", + APIVersion: "v1beta1", + }, + ObjectMeta: api.ObjectMeta{ + Name: "dns-test-" + string(util.NewUUID()), + }, + Spec: api.PodSpec{ + Volumes: []api.Volume{ + { + Name: "results", + Source: api.VolumeSource{ + EmptyDir: &api.EmptyDir{}, + }, + }, + }, + Containers: []api.Container{ + { + Name: "webserver", + Image: "kubernetes/test-webserver", + VolumeMounts: []api.VolumeMount{ + { + Name: "results", + MountPath: "/results", + }, + }, + }, + { + Name: "pinger", + Image: "busybox", + Command: []string{"sh", "-c", probeCmd}, + VolumeMounts: []api.VolumeMount{ + { + Name: "results", + MountPath: "/results", + }, + }, + }, + }, + }, + } + + By("submitting the pod to kuberenetes") + _, err := podClient.Create(pod) + if err != nil { + Fail(fmt.Sprintf("Failed to create %s pod: %v", pod.Name, err)) + } + defer func() { + By("deleting the pod") + defer GinkgoRecover() + podClient.Delete(pod.Name) + }() + + + By("waiting for the pod to start running") + waitForPodRunning(c, pod.Name) + + By("retrieving the pod") + pod, err = podClient.Get(pod.Name) + if err != nil { + Fail(fmt.Sprintf("Failed to get pod %s: %v", pod.Name, err)) + } + + // Try to find results for each expected name. + By("looking for the results for each expected name") + var failed []string + for try := 1; try < 100; try++ { + failed = []string{} + for _, name := range namesToResolve { + _, err := c.Get(). + Prefix("proxy"). + Resource("pods"). + Namespace("default"). + Name(pod.Name). + Suffix("results", name). + Do().Raw() + if err != nil { + failed = append(failed, name) + fmt.Printf("Lookup using %s for %s failed: %v\n", pod.Name, name, err) + } + } + if len(failed) == 0 { + break + } + fmt.Printf("lookups using %s failed for: %v\n", pod.Name, failed) + time.Sleep(10 * time.Second) + } + Expect(len(failed)).To(Equal(0)) + + // TODO: probe from the host, too. + + fmt.Printf("DNS probes using %s succeeded\n", pod.Name) } - if len(failed) == 0 { - break - } - glog.Infof("lookups using %s failed for: %v", pod.Name, failed) - time.Sleep(10 * time.Second) - } - if len(failed) != 0 { - glog.Errorf("DNS using %s failed for: %v", pod.Name, failed) - return false - } - - // TODO: probe from the host, too. - - glog.Infof("DNS probes using %s succeeded", pod.Name) - return true -} - -var _ = Describe("TestClusterDNS", func() { - It("should pass", func() { - // TODO: Instead of OrDie, client should Fail the test if there's a problem. - // In general tests should Fail() instead of glog.Fatalf(). - Expect(TestClusterDNS(loadClientOrDie())).To(BeTrue()) }) }) From eefd7ace56915413f0cc9d1638a79065e1dafdad Mon Sep 17 00:00:00 2001 From: Robert Rati Date: Thu, 5 Feb 2015 08:59:41 -0500 Subject: [PATCH 29/50] Converted TestKubernetesROService to native ginkgo syntax #4162 --- test/e2e/roservice.go | 64 +++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 36 deletions(-) diff --git a/test/e2e/roservice.go b/test/e2e/roservice.go index a631fbcdb9c..421cbcdee7a 100644 --- a/test/e2e/roservice.go +++ b/test/e2e/roservice.go @@ -17,50 +17,42 @@ limitations under the License. package e2e import ( + "fmt" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/client" - "github.com/golang/glog" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) -func TestKubernetesROService(c *client.Client) bool { - svc := api.ServiceList{} - err := c.Get(). - Namespace("default"). - AbsPath("/api/v1beta1/proxy/services/kubernetes-ro/api/v1beta1/services"). - Do(). - Into(&svc) - if err != nil { - glog.Errorf("unexpected error listing services using ro service: %v", err) - return false - } - var foundRW, foundRO bool - for i := range svc.Items { - if svc.Items[i].Name == "kubernetes" { - foundRW = true - } - if svc.Items[i].Name == "kubernetes-ro" { - foundRO = true - } - } - if !foundRW { - glog.Error("no RW service found") - } - if !foundRO { - glog.Error("no RO service found") - } - if !foundRW || !foundRO { - return false - } - return true -} - var _ = Describe("TestKubernetesROService", func() { + var c *client.Client + + BeforeEach(func() { + c = loadClientOrDie() + }) + It("should pass", func() { - // TODO: Instead of OrDie, client should Fail the test if there's a problem. - // In general tests should Fail() instead of glog.Fatalf(). - Expect(TestKubernetesROService(loadClientOrDie())).To(BeTrue()) + svc := api.ServiceList{} + err := c.Get(). + Namespace("default"). + AbsPath("/api/v1beta1/proxy/services/kubernetes-ro/api/v1beta1/services"). + Do(). + Into(&svc) + if err != nil { + Fail(fmt.Sprintf("unexpected error listing services using ro service: %v", err)) + } + var foundRW, foundRO bool + for i := range svc.Items { + if svc.Items[i].Name == "kubernetes" { + foundRW = true + } + if svc.Items[i].Name == "kubernetes-ro" { + foundRO = true + } + } + Expect(foundRW).To(Equal(true)) + Expect(foundRO).To(Equal(true)) }) }) From d3e04cd2460c0ad4a7392bf37e39aa507a0924dd Mon Sep 17 00:00:00 2001 From: Robert Rati Date: Thu, 5 Feb 2015 09:05:45 -0500 Subject: [PATCH 30/50] Moved the ROService test into the services suite #4162 --- test/e2e/roservice.go | 58 ------------------------------------------- test/e2e/service.go | 23 +++++++++++++++++ 2 files changed, 23 insertions(+), 58 deletions(-) delete mode 100644 test/e2e/roservice.go diff --git a/test/e2e/roservice.go b/test/e2e/roservice.go deleted file mode 100644 index 421cbcdee7a..00000000000 --- a/test/e2e/roservice.go +++ /dev/null @@ -1,58 +0,0 @@ -/* -Copyright 2014 Google Inc. All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package e2e - -import ( - "fmt" - - "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - "github.com/GoogleCloudPlatform/kubernetes/pkg/client" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("TestKubernetesROService", func() { - var c *client.Client - - BeforeEach(func() { - c = loadClientOrDie() - }) - - It("should pass", func() { - svc := api.ServiceList{} - err := c.Get(). - Namespace("default"). - AbsPath("/api/v1beta1/proxy/services/kubernetes-ro/api/v1beta1/services"). - Do(). - Into(&svc) - if err != nil { - Fail(fmt.Sprintf("unexpected error listing services using ro service: %v", err)) - } - var foundRW, foundRO bool - for i := range svc.Items { - if svc.Items[i].Name == "kubernetes" { - foundRW = true - } - if svc.Items[i].Name == "kubernetes-ro" { - foundRO = true - } - } - Expect(foundRW).To(Equal(true)) - Expect(foundRO).To(Equal(true)) - }) -}) diff --git a/test/e2e/service.go b/test/e2e/service.go index 4880615720b..781fbfdea37 100644 --- a/test/e2e/service.go +++ b/test/e2e/service.go @@ -154,4 +154,27 @@ var _ = Describe("Services", func() { fmt.Printf("DNS probes using %s succeeded\n", pod.Name) } }) + + It("should provide RW and RO services", func() { + svc := api.ServiceList{} + err := c.Get(). + Namespace("default"). + AbsPath("/api/v1beta1/proxy/services/kubernetes-ro/api/v1beta1/services"). + Do(). + Into(&svc) + if err != nil { + Fail(fmt.Sprintf("unexpected error listing services using ro service: %v", err)) + } + var foundRW, foundRO bool + for i := range svc.Items { + if svc.Items[i].Name == "kubernetes" { + foundRW = true + } + if svc.Items[i].Name == "kubernetes-ro" { + foundRO = true + } + } + Expect(foundRW).To(Equal(true)) + Expect(foundRO).To(Equal(true)) + }) }) From 688f96cd3314664de0653665699dab5672e57cb7 Mon Sep 17 00:00:00 2001 From: Zach Loafman Date: Wed, 4 Feb 2015 16:57:53 -0800 Subject: [PATCH 31/50] Run shell tests under Ginkgo, changing all reporting to JUnit * Add a test/e2e/shell.go that slurps up everything in hack/e2e-suite and runs it as a bash test, borrowing all the code from hack/e2e.go. * Rip out all the crap in hack/e2e.go that deal with multiple tests * Move hack/e2e-suite/goe2e.sh to hack/ginkgo-e2e.sh so that it doesn't get slurped up. --- hack/e2e.go | 156 +-------------------- hack/{e2e-suite/goe2e.sh => ginkgo-e2e.sh} | 2 +- hack/jenkins/e2e.sh | 14 +- test/e2e/shell.go | 71 ++++++++++ 4 files changed, 83 insertions(+), 160 deletions(-) rename hack/{e2e-suite/goe2e.sh => ginkgo-e2e.sh} (98%) create mode 100644 test/e2e/shell.go diff --git a/hack/e2e.go b/hack/e2e.go index 0ca85d26f67..bfdeccd526c 100644 --- a/hack/e2e.go +++ b/hack/e2e.go @@ -30,10 +30,8 @@ import ( "os/signal" "path" "path/filepath" - "sort" "strconv" "strings" - "time" ) var ( @@ -44,12 +42,8 @@ var ( push = flag.Bool("push", false, "If true, push to e2e cluster. Has no effect if -up is true.") pushup = flag.Bool("pushup", false, "If true, push to e2e cluster if it's up, otherwise start the e2e cluster.") down = flag.Bool("down", false, "If true, tear down the cluster before exiting.") - orderseed = flag.Int64("orderseed", 0, "If non-zero, seed of random test shuffle order. (Otherwise random.)") - test = flag.Bool("test", false, "Run all tests in hack/e2e-suite.") - tests = flag.String("tests", "", "Run only tests in hack/e2e-suite matching this glob. Ignored if -test is set.") - times = flag.Int("times", 1, "Number of times each test is eligible to be run. Individual order is determined by shuffling --times instances of each test using --orderseed (like a multi-deck shoe of cards).") + test = flag.Bool("test", false, "Run Ginkgo tests.") root = flag.String("root", absOrDie(filepath.Clean(filepath.Join(path.Base(os.Args[0]), ".."))), "Root directory of kubernetes repository.") - tap = flag.Bool("tap", false, "Enable Test Anything Protocol (TAP) output (disables --verbose, only failure output recorded)") verbose = flag.Bool("v", false, "If true, print all command output.") trace_bash = flag.Bool("trace-bash", false, "If true, pass -x to bash to trace all bash commands") checkVersionSkew = flag.Bool("check_version_skew", true, ""+ @@ -95,21 +89,6 @@ func main() { flag.Parse() signal.Notify(signals, os.Interrupt) - if *tap { - fmt.Printf("TAP version 13\n") - log.SetPrefix("# ") - - // TODO: this limitation is fixable by moving runBash to - // outputing to temp files, which still lets people check on - // stuck things interactively. The current stdout/stderr - // approach isn't really going to work with TAP, though. - *verbose = false - } - - if *test { - *tests = "*" - } - if *isup { status := 1 if IsUp() { @@ -168,8 +147,8 @@ func main() { switch { case *ctlCmd != "": failure = !runBash("'kubectl "+*ctlCmd+"'", "$KUBECTL "+*ctlCmd) - case *tests != "": - failure = PrintResults(Test()) + case *test: + failure = Test() } if *down { @@ -278,7 +257,7 @@ func shuffleStrings(strings []string, r *rand.Rand) { } } -func Test() (results ResultsByTest) { +func Test() bool { defer runBashUntil("watchEvents", "while true; do $KUBECTL --watch-only get events; done")() if !IsUp() { @@ -287,128 +266,7 @@ func Test() (results ResultsByTest) { ValidateClusterSize() - // run tests! - dir, err := os.Open(filepath.Join(*root, "hack", "e2e-suite")) - if err != nil { - log.Fatal("Couldn't open e2e-suite dir") - } - defer dir.Close() - names, err := dir.Readdirnames(0) - if err != nil { - log.Fatal("Couldn't read names in e2e-suite dir") - } - - toRun := make([]string, 0, len(names)) - for i := range names { - name := names[i] - if name == "." || name == ".." { - continue - } - if match, err := path.Match(*tests, name); !match && err == nil { - continue - } - if err != nil { - log.Fatalf("Bad test pattern: %v", *tests) - } - toRun = append(toRun, name) - } - - if *orderseed == 0 { - // Use low order bits of NanoTime as the default seed. (Using - // all the bits makes for a long, very similar looking seed - // between runs.) - *orderseed = time.Now().UnixNano() & (1<<32 - 1) - } - sort.Strings(toRun) - if *times != 1 { - if *times <= 0 { - log.Fatal("Invalid --times (negative or no testing requested)!") - } - newToRun := make([]string, 0, *times*len(toRun)) - for i := 0; i < *times; i++ { - newToRun = append(newToRun, toRun...) - } - toRun = newToRun - } - shuffleStrings(toRun, rand.New(rand.NewSource(*orderseed))) - log.Printf("Running tests matching %v shuffled with seed %#x: %v", *tests, *orderseed, toRun) - results = ResultsByTest{} - if *tap { - fmt.Printf("1..%v\n", len(toRun)) - } - for i, name := range toRun { - absName := filepath.Join(*root, "hack", "e2e-suite", name) - log.Printf("Starting test [%v/%v]: %v", i+1, len(toRun), name) - start := time.Now() - testResult := results[name] - res, stdout, stderr := runBashWithOutputs(name, absName) - // The duration_ms output is an undocumented Jenkins TAP - // plugin feature for test duration. One might think _ms means - // milliseconds, but Jenkins interprets this field in seconds. - duration_secs := time.Now().Sub(start).Seconds() - if res { - fmt.Printf("ok %v - %v\n", i+1, name) - if *tap { - fmt.Printf(" ---\n duration_ms: %.3f\n ...\n", duration_secs) - } - testResult.Pass++ - } else { - fmt.Printf("not ok %v - %v\n", i+1, name) - if *tap { - fmt.Printf(" ---\n duration_ms: %.3f\n", duration_secs) - } - printBashOutputs(" ", " ", stdout, stderr, *tap) - if *tap { - fmt.Printf(" ...\n") - } - testResult.Fail++ - } - results[name] = testResult - } - - return -} - -func PrintResults(results ResultsByTest) bool { - failures := 0 - - passed := []string{} - flaky := []string{} - failed := []string{} - for test, result := range results { - if result.Pass > 0 && result.Fail == 0 { - passed = append(passed, test) - } else if result.Pass > 0 && result.Fail > 0 { - flaky = append(flaky, test) - failures += result.Fail - } else { - failed = append(failed, test) - failures += result.Fail - } - } - sort.Strings(passed) - sort.Strings(flaky) - sort.Strings(failed) - printSubreport("Passed", passed, results) - printSubreport("Flaky", flaky, results) - printSubreport("Failed", failed, results) - if failures > 0 { - log.Printf("%v test(s) failed.", failures) - } else { - log.Printf("Success!") - } - - return failures > 0 -} - -func printSubreport(title string, tests []string, results ResultsByTest) { - report := title + " tests:" - - for _, test := range tests { - result := results[test] - report += fmt.Sprintf(" %v[%v/%v]", test, result.Pass, result.Pass+result.Fail) - } - log.Printf(report) + return runBash("Ginkgo tests", filepath.Join(*root, "hack", "ginkgo-e2e.sh")) } // All nonsense below is temporary until we have go versions of these things. @@ -451,10 +309,6 @@ func runBashUntil(stepName, bashFragment string) func() { cmd.Process.Signal(os.Interrupt) headerprefix := stepName + " " lineprefix := " " - if *tap { - headerprefix = "# " + headerprefix - lineprefix = "# " + lineprefix - } printBashOutputs(headerprefix, lineprefix, string(stdout.Bytes()), string(stderr.Bytes()), false) } } diff --git a/hack/e2e-suite/goe2e.sh b/hack/ginkgo-e2e.sh similarity index 98% rename from hack/e2e-suite/goe2e.sh rename to hack/ginkgo-e2e.sh index daffe8830c1..8af313b066b 100755 --- a/hack/e2e-suite/goe2e.sh +++ b/hack/ginkgo-e2e.sh @@ -18,7 +18,7 @@ set -o errexit set -o nounset set -o pipefail -KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../.. +KUBE_ROOT=$(dirname "${BASH_SOURCE}")/.. : ${KUBE_VERSION_ROOT:=${KUBE_ROOT}} : ${KUBECTL:="${KUBE_VERSION_ROOT}/cluster/kubectl.sh"} diff --git a/hack/jenkins/e2e.sh b/hack/jenkins/e2e.sh index 25e8d50362e..9b363e221f7 100755 --- a/hack/jenkins/e2e.sh +++ b/hack/jenkins/e2e.sh @@ -77,11 +77,9 @@ if [[ ! -z ${E2E_SET_CLUSTER_API_VERSION:-} ]]; then export CLUSTER_API_VERSION=$(echo ${GITHASH} | cut -c 2-) fi -# Have cmd/e2e run by goe2e.sh generate JUnit report in ${WORKSPACE}/junit*.xml -export E2E_REPORT_DIR=${WORKSPACE} - -go run ./hack/e2e.go ${E2E_OPT} -v --down -go run ./hack/e2e.go ${E2E_OPT} -v --up -go run ./hack/e2e.go -v --ctl="version --match-server-version=false" -go run ./hack/e2e.go ${E2E_OPT} --test --tap | tee ../e2e.${JOB_NAME}.${BUILD_NUMBER}.${GITHASH}.tap -go run ./hack/e2e.go ${E2E_OPT} -v --down +export KUBE_CONFIG_FILE="config-test.sh" +cluster/kube-down.sh +cluster/kube-up.sh +cluster/kubectl.sh version +hack/ginkgo-e2e.sh --report_dir=${WORKSPACE} +cluster/kube-down.sh diff --git a/test/e2e/shell.go b/test/e2e/shell.go new file mode 100644 index 00000000000..a91dad1faa3 --- /dev/null +++ b/test/e2e/shell.go @@ -0,0 +1,71 @@ +/* +Copyright 2015 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "os/exec" + "path" + "path/filepath" + + . "github.com/onsi/ginkgo" +) + +var ( + root = absOrDie(filepath.Clean(filepath.Join(path.Base(os.Args[0]), ".."))) +) + +var _ = Describe("Shell", func() { + // Slurp up all the tests in hack/e2e-suite + bashE2ERoot := filepath.Join(root, "hack/e2e-suite") + files, err := ioutil.ReadDir(bashE2ERoot) + if err != nil { + Fail(err.Error()) + } + + for _, file := range files { + fileName := file.Name() // Make a copy + It(fmt.Sprintf("tests that %v passes", fileName), func() { + runCmdTest(filepath.Join(bashE2ERoot, fileName)) + }) + } +}) + +func absOrDie(path string) string { + out, err := filepath.Abs(path) + if err != nil { + panic(err) + } + return out +} + +// Runs the given cmd test. +func runCmdTest(path string) { + By(fmt.Sprintf("Running %v", path)) + cmd := exec.Command(path) + cmd.Stdout = bytes.NewBuffer(nil) + cmd.Stderr = cmd.Stdout + + if err := cmd.Run(); err != nil { + Fail(fmt.Sprintf("Error running %v:\nCommand output:\n%v\n", cmd, cmd.Stdout)) + return + } + return +} From bace7593d0a3a4258fd80a400c73c54c8ef025d3 Mon Sep 17 00:00:00 2001 From: Zach Loafman Date: Wed, 4 Feb 2015 17:07:20 -0800 Subject: [PATCH 32/50] Enable ginkgo.v (console output is missing spec without it) --- hack/ginkgo-e2e.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/hack/ginkgo-e2e.sh b/hack/ginkgo-e2e.sh index 8af313b066b..c0ef4c57f82 100755 --- a/hack/ginkgo-e2e.sh +++ b/hack/ginkgo-e2e.sh @@ -102,5 +102,6 @@ fi "${e2e}" "${auth_config[@]:+${auth_config[@]}}" \ --host="https://${KUBE_MASTER_IP-}" \ --provider="${KUBERNETES_PROVIDER}" \ + --ginkgo.v \ ${E2E_REPORT_DIR+"--report_dir=${E2E_REPORT_DIR}"} \ "${@}" From f21a25e7fc84645082376f8c374ed723b1888fd1 Mon Sep 17 00:00:00 2001 From: Zach Loafman Date: Thu, 5 Feb 2015 08:06:50 -0800 Subject: [PATCH 33/50] Add hack/ginkgo-e2e.sh to kubernetes-test.tgz --- hack/lib/golang.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/hack/lib/golang.sh b/hack/lib/golang.sh index 4a6598c8a6d..d81f7e6c46e 100644 --- a/hack/lib/golang.sh +++ b/hack/lib/golang.sh @@ -55,6 +55,7 @@ readonly KUBE_TEST_PORTABLE=( contrib/for-tests/network-tester/service.json hack/e2e.go hack/e2e-suite + hack/ginkgo-e2e.sh ) # If we update this we need to also update the set of golang compilers we build From 2f33c06c210ee6c3dc70225a9605b10249ff1dff Mon Sep 17 00:00:00 2001 From: Zach Loafman Date: Thu, 5 Feb 2015 08:13:03 -0800 Subject: [PATCH 34/50] Remove cluster/test-network.sh To fix this, it would be an alias for: hack/ginkgo-e2e.sh -t TestNetwork ... which may not work on binary distributions, because it's relying on test assets, so it probably shouldn't live in cluster/ ... --- cluster/test-network.sh | 23 ----------------------- 1 file changed, 23 deletions(-) delete mode 100755 cluster/test-network.sh diff --git a/cluster/test-network.sh b/cluster/test-network.sh deleted file mode 100755 index df456caead7..00000000000 --- a/cluster/test-network.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -# Copyright 2014 Google Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -o errexit -set -o nounset -set -o pipefail - -KUBE_ROOT=$(dirname "${BASH_SOURCE}")/.. - -exec "${KUBE_ROOT}/hack/e2e-suite/goe2e.sh" -t TestNetwork From 4b309aa8644dd3932ef2dfdd4e570c1dc313669a Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 5 Feb 2015 11:36:55 -0500 Subject: [PATCH 35/50] Do not use {} in systemd environment variables From the systemd man page: Use "${FOO}" as part of a word, or as a word of its own, on the command line, in which case it will be replaced by the value of the environment variable including all whitespace it contains, resulting in a single argument. Use "$FOO" as a separate word on the command line, in which case it will be replaced by the value of the environment variable split at whitespace, resulting in zero or more arguments. Since we want people to be able to use these for multiple arguments we need to make sure we don't use {} around the env vars... --- contrib/init/systemd/kube-apiserver.service | 18 +++++++++--------- .../systemd/kube-controller-manager.service | 10 +++++----- contrib/init/systemd/kube-proxy.service | 8 ++++---- contrib/init/systemd/kube-scheduler.service | 8 ++++---- contrib/init/systemd/kubelet.service | 16 ++++++++-------- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/contrib/init/systemd/kube-apiserver.service b/contrib/init/systemd/kube-apiserver.service index eef9fbf96fa..7d947fe9cae 100644 --- a/contrib/init/systemd/kube-apiserver.service +++ b/contrib/init/systemd/kube-apiserver.service @@ -7,15 +7,15 @@ EnvironmentFile=-/etc/kubernetes/config EnvironmentFile=-/etc/kubernetes/apiserver User=kube ExecStart=/usr/bin/kube-apiserver \ - ${KUBE_LOGTOSTDERR} \ - ${KUBE_LOG_LEVEL} \ - ${KUBE_ETCD_SERVERS} \ - ${KUBE_API_ADDRESS} \ - ${KUBE_API_PORT} \ - ${KUBELET_PORT} \ - ${KUBE_ALLOW_PRIV} \ - ${KUBE_SERVICE_ADDRESSES} \ - ${KUBE_API_ARGS} + $KUBE_LOGTOSTDERR \ + $KUBE_LOG_LEVEL \ + $KUBE_ETCD_SERVERS \ + $KUBE_API_ADDRESS \ + $KUBE_API_PORT \ + $KUBELET_PORT \ + $KUBE_ALLOW_PRIV \ + $KUBE_SERVICE_ADDRESSES \ + $KUBE_API_ARGS Restart=on-failure [Install] diff --git a/contrib/init/systemd/kube-controller-manager.service b/contrib/init/systemd/kube-controller-manager.service index 14d5ee5825d..872115c4f97 100644 --- a/contrib/init/systemd/kube-controller-manager.service +++ b/contrib/init/systemd/kube-controller-manager.service @@ -8,11 +8,11 @@ EnvironmentFile=-/etc/kubernetes/apiserver EnvironmentFile=-/etc/kubernetes/controller-manager User=kube ExecStart=/usr/bin/kube-controller-manager \ - ${KUBE_LOGTOSTDERR} \ - ${KUBE_LOG_LEVEL} \ - ${KUBELET_ADDRESSES} \ - ${KUBE_MASTER} \ - ${KUBE_CONTROLLER_MANAGER_ARGS} + $KUBE_LOGTOSTDERR \ + $KUBE_LOG_LEVEL \ + $KUBELET_ADDRESSES \ + $KUBE_MASTER \ + $KUBE_CONTROLLER_MANAGER_ARGS Restart=on-failure [Install] diff --git a/contrib/init/systemd/kube-proxy.service b/contrib/init/systemd/kube-proxy.service index fcfc2e276c0..921f4ceee33 100644 --- a/contrib/init/systemd/kube-proxy.service +++ b/contrib/init/systemd/kube-proxy.service @@ -6,10 +6,10 @@ Documentation=https://github.com/GoogleCloudPlatform/kubernetes EnvironmentFile=-/etc/kubernetes/config EnvironmentFile=-/etc/kubernetes/proxy ExecStart=/usr/bin/kube-proxy \ - ${KUBE_LOGTOSTDERR} \ - ${KUBE_LOG_LEVEL} \ - ${KUBE_ETCD_SERVERS} \ - ${KUBE_PROXY_ARGS} + $KUBE_LOGTOSTDERR \ + $KUBE_LOG_LEVEL \ + $KUBE_ETCD_SERVERS \ + $KUBE_PROXY_ARGS Restart=on-failure [Install] diff --git a/contrib/init/systemd/kube-scheduler.service b/contrib/init/systemd/kube-scheduler.service index 7ca88417e8f..f04666ebf7f 100644 --- a/contrib/init/systemd/kube-scheduler.service +++ b/contrib/init/systemd/kube-scheduler.service @@ -8,10 +8,10 @@ EnvironmentFile=-/etc/kubernetes/apiserver EnvironmentFile=-/etc/kubernetes/scheduler User=kube ExecStart=/usr/bin/kube-scheduler \ - ${KUBE_LOGTOSTDERR} \ - ${KUBE_LOG_LEVEL} \ - ${KUBE_MASTER} \ - ${KUBE_SCHEDULER_ARGS} + $KUBE_LOGTOSTDERR \ + $KUBE_LOG_LEVEL \ + $KUBE_MASTER \ + $KUBE_SCHEDULER_ARGS Restart=on-failure [Install] diff --git a/contrib/init/systemd/kubelet.service b/contrib/init/systemd/kubelet.service index 973db5b608e..9c43f50ef1c 100644 --- a/contrib/init/systemd/kubelet.service +++ b/contrib/init/systemd/kubelet.service @@ -9,14 +9,14 @@ WorkingDirectory=/var/lib/kubelet EnvironmentFile=-/etc/kubernetes/config EnvironmentFile=-/etc/kubernetes/kubelet ExecStart=/usr/bin/kubelet \ - ${KUBE_LOGTOSTDERR} \ - ${KUBE_LOG_LEVEL} \ - ${KUBE_ETCD_SERVERS} \ - ${KUBELET_ADDRESS} \ - ${KUBELET_PORT} \ - ${KUBELET_HOSTNAME} \ - ${KUBE_ALLOW_PRIV} \ - ${KUBELET_ARGS} + $KUBE_LOGTOSTDERR \ + $KUBE_LOG_LEVEL \ + $KUBE_ETCD_SERVERS \ + $KUBELET_ADDRESS \ + $KUBELET_PORT \ + $KUBELET_HOSTNAME \ + $KUBE_ALLOW_PRIV \ + $KUBELET_ARGS Restart=on-failure [Install] From 0b456e40bfb3f3a0ae3996ceeaf348636db00c3a Mon Sep 17 00:00:00 2001 From: Zach Loafman Date: Thu, 5 Feb 2015 09:13:56 -0800 Subject: [PATCH 36/50] Fix jenkins/hack/e2e.sh, log errors better --- cluster/kube-up.sh | 2 ++ hack/jenkins/e2e.sh | 10 +++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/cluster/kube-up.sh b/cluster/kube-up.sh index 37d59d9f59d..6972ad80a70 100755 --- a/cluster/kube-up.sh +++ b/cluster/kube-up.sh @@ -46,3 +46,5 @@ echo "... calling setup-logging-firewall" >&2 setup-logging-firewall echo "Done" >&2 + +exit 0 diff --git a/hack/jenkins/e2e.sh b/hack/jenkins/e2e.sh index 9b363e221f7..1a0dde2b939 100755 --- a/hack/jenkins/e2e.sh +++ b/hack/jenkins/e2e.sh @@ -78,8 +78,8 @@ if [[ ! -z ${E2E_SET_CLUSTER_API_VERSION:-} ]]; then fi export KUBE_CONFIG_FILE="config-test.sh" -cluster/kube-down.sh -cluster/kube-up.sh -cluster/kubectl.sh version -hack/ginkgo-e2e.sh --report_dir=${WORKSPACE} -cluster/kube-down.sh +cluster/kube-down.sh || true +cluster/kube-up.sh || { echo "-- kube-up failed --"; exit 1; } +cluster/kubectl.sh version || { echo "-- kubectl failed --"; exit 1; } +hack/ginkgo-e2e.sh --report_dir=${WORKSPACE} || { echo "-- tests failed --"; exit 1; } +cluster/kube-down.sh || { echo "-- kube-down failed --"; exit 1; } From 3efb4527f434aceaac44dfaf452a27745230af1a Mon Sep 17 00:00:00 2001 From: Mike Schiller Date: Thu, 5 Feb 2015 12:27:41 -0500 Subject: [PATCH 37/50] inserted mising = in kube-apiserver's portal_net parameter that prevented the kube-apiserver from starting --- docs/getting-started-guides/aws/cloudformation-template.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started-guides/aws/cloudformation-template.json b/docs/getting-started-guides/aws/cloudformation-template.json index 87eab68c9c1..510f9f155c0 100644 --- a/docs/getting-started-guides/aws/cloudformation-template.json +++ b/docs/getting-started-guides/aws/cloudformation-template.json @@ -247,7 +247,7 @@ " ExecStart=/opt/bin/kube-apiserver \\\n", " --address=0.0.0.0 \\\n", " --port=8080 \\\n", - " --portal_net 10.244.0.0/16 \\\n", + " --portal_net=10.244.0.0/16 \\\n", " --etcd_servers=http://127.0.0.1:4001 \\\n", " --public_address_override=$private_ipv4 \\\n", " --logtostderr=true\n", From d46ae5d841711906fc2b2f5358b96e93f09760f7 Mon Sep 17 00:00:00 2001 From: Jeff Lowdermilk Date: Wed, 4 Feb 2015 16:14:48 -0800 Subject: [PATCH 38/50] Refactor kubectl/cmd helpers into pkg/kubectl/cmd/util Allows helpers to be used by config commands. --- pkg/kubectl/cmd/cmd.go | 66 ++++++++++++++++++--- pkg/kubectl/cmd/cmd_test.go | 41 +++++++++++++ pkg/kubectl/cmd/create.go | 2 +- pkg/kubectl/cmd/delete.go | 5 +- pkg/kubectl/cmd/describe.go | 3 +- pkg/kubectl/cmd/expose.go | 22 +++---- pkg/kubectl/cmd/get.go | 19 +++--- pkg/kubectl/cmd/log.go | 3 +- pkg/kubectl/cmd/printing_test.go | 67 ---------------------- pkg/kubectl/cmd/proxy.go | 7 ++- pkg/kubectl/cmd/resize.go | 9 +-- pkg/kubectl/cmd/rollingupdate.go | 13 +++-- pkg/kubectl/cmd/run.go | 13 +++-- pkg/kubectl/cmd/stop.go | 3 +- pkg/kubectl/cmd/update.go | 9 +-- pkg/kubectl/cmd/{ => util}/helpers.go | 14 ++++- pkg/kubectl/cmd/{ => util}/helpers_test.go | 2 +- pkg/kubectl/cmd/{ => util}/printing.go | 61 +------------------- pkg/kubectl/cmd/{ => util}/resource.go | 2 +- pkg/kubectl/cmd/version.go | 3 +- 20 files changed, 179 insertions(+), 185 deletions(-) delete mode 100644 pkg/kubectl/cmd/printing_test.go rename pkg/kubectl/cmd/{ => util}/helpers.go (95%) rename pkg/kubectl/cmd/{ => util}/helpers_test.go (99%) rename pkg/kubectl/cmd/{ => util}/printing.go (50%) rename pkg/kubectl/cmd/{ => util}/resource.go (99%) diff --git a/pkg/kubectl/cmd/cmd.go b/pkg/kubectl/cmd/cmd.go index 53468cb0faf..2c186684a75 100644 --- a/pkg/kubectl/cmd/cmd.go +++ b/pkg/kubectl/cmd/cmd.go @@ -29,6 +29,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" cmdconfig "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/config" + cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" @@ -143,7 +144,7 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { return kubectl.ReaperFor(mapping.Kind, client) }, Validator: func(cmd *cobra.Command) (validation.Schema, error) { - if GetFlagBool(cmd, "validate") { + if cmdutil.GetFlagBool(cmd, "validate") { client, err := clients.ClientForVersion("") if err != nil { return nil, err @@ -217,6 +218,62 @@ Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`, return cmds } +// PrintObject prints an api object given command line flags to modify the output format +func (f *Factory) PrintObject(cmd *cobra.Command, obj runtime.Object, out io.Writer) error { + mapper, _ := f.Object(cmd) + _, kind, err := api.Scheme.ObjectVersionAndKind(obj) + if err != nil { + return err + } + + mapping, err := mapper.RESTMapping(kind) + if err != nil { + return err + } + + printer, err := f.PrinterForMapping(cmd, mapping) + if err != nil { + return err + } + return printer.PrintObj(obj, out) +} + +// PrinterForMapping returns a printer suitable for displaying the provided resource type. +// Requires that printer flags have been added to cmd (see AddPrinterFlags). +func (f *Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.ResourcePrinter, error) { + printer, ok, err := cmdutil.PrinterForCommand(cmd) + if err != nil { + return nil, err + } + if ok { + clientConfig, err := f.ClientConfig(cmd) + checkErr(err) + defaultVersion := clientConfig.Version + + version := cmdutil.OutputVersion(cmd, defaultVersion) + if len(version) == 0 { + version = mapping.APIVersion + } + if len(version) == 0 { + return nil, fmt.Errorf("you must specify an output-version when using this output format") + } + printer = kubectl.NewVersionedPrinter(printer, mapping.ObjectConvertor, version) + } else { + printer, err = f.Printer(cmd, mapping, cmdutil.GetFlagBool(cmd, "no-headers")) + if err != nil { + return nil, err + } + } + return printer, nil +} + +// ClientMapperForCommand returns a ClientMapper for the given command and factory. +func (f *Factory) ClientMapperForCommand(cmd *cobra.Command) resource.ClientMapper { + return resource.ClientMapperFunc(func(mapping *meta.RESTMapping) (resource.RESTClient, error) { + return f.RESTClient(cmd, mapping) + }) +} + // DefaultClientConfig creates a clientcmd.ClientConfig with the following hierarchy: // 1. Use the kubeconfig builder. The number of merges and overrides here gets a little crazy. Stay with me. // 1. Merge together the kubeconfig itself. This is done with the following hierarchy and merge rules: @@ -266,13 +323,6 @@ func DefaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig { return clientConfig } -// ClientMapperForCommand returns a ClientMapper for the given command and factory. -func ClientMapperForCommand(cmd *cobra.Command, f *Factory) resource.ClientMapper { - return resource.ClientMapperFunc(func(mapping *meta.RESTMapping) (resource.RESTClient, error) { - return f.RESTClient(cmd, mapping) - }) -} - func checkErr(err error) { if err != nil { glog.FatalDepth(1, err) diff --git a/pkg/kubectl/cmd/cmd_test.go b/pkg/kubectl/cmd/cmd_test.go index ef5dc0bc674..e813e9f7414 100644 --- a/pkg/kubectl/cmd/cmd_test.go +++ b/pkg/kubectl/cmd/cmd_test.go @@ -21,6 +21,7 @@ import ( "fmt" "io" "io/ioutil" + "os" "testing" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" @@ -203,3 +204,43 @@ func TestClientVersions(t *testing.T) { } } } + +func ExamplePrintReplicationController() { + f, tf, codec := NewAPIFactory() + tf.Printer = kubectl.NewHumanReadablePrinter(false) + tf.Client = &client.FakeRESTClient{ + Codec: codec, + Client: nil, + } + cmd := f.NewCmdRunContainer(os.Stdout) + ctrl := &api.ReplicationController{ + ObjectMeta: api.ObjectMeta{ + Name: "foo", + Labels: map[string]string{"foo": "bar"}, + }, + Spec: api.ReplicationControllerSpec{ + Replicas: 1, + Selector: map[string]string{"foo": "bar"}, + Template: &api.PodTemplateSpec{ + ObjectMeta: api.ObjectMeta{ + Labels: map[string]string{"foo": "bar"}, + }, + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: "foo", + Image: "someimage", + }, + }, + }, + }, + }, + } + err := f.PrintObject(cmd, ctrl, os.Stdout) + if err != nil { + fmt.Printf("Unexpected error: %v", err) + } + // Output: + // CONTROLLER CONTAINER(S) IMAGE(S) SELECTOR REPLICAS + // foo foo someimage foo=bar 1 +} diff --git a/pkg/kubectl/cmd/create.go b/pkg/kubectl/cmd/create.go index 1038560eed6..07fef112291 100644 --- a/pkg/kubectl/cmd/create.go +++ b/pkg/kubectl/cmd/create.go @@ -51,7 +51,7 @@ Examples: checkErr(err) mapper, typer := f.Object(cmd) - r := resource.NewBuilder(mapper, typer, ClientMapperForCommand(cmd, f)). + r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand(cmd)). ContinueOnError(). NamespaceParam(cmdNamespace).RequireNamespace(). FilenameParam(flags.Filenames...). diff --git a/pkg/kubectl/cmd/delete.go b/pkg/kubectl/cmd/delete.go index 94704056388..753ecf69314 100644 --- a/pkg/kubectl/cmd/delete.go +++ b/pkg/kubectl/cmd/delete.go @@ -23,6 +23,7 @@ import ( "github.com/spf13/cobra" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" + cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" ) @@ -62,11 +63,11 @@ Examples: checkErr(err) mapper, typer := f.Object(cmd) - r := resource.NewBuilder(mapper, typer, ClientMapperForCommand(cmd, f)). + r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand(cmd)). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(flags.Filenames...). - SelectorParam(GetFlagString(cmd, "selector")). + SelectorParam(cmdutil.GetFlagString(cmd, "selector")). ResourceTypeOrNameArgs(args...). Flatten(). Do() diff --git a/pkg/kubectl/cmd/describe.go b/pkg/kubectl/cmd/describe.go index 7950d57cb73..a8770afda7e 100644 --- a/pkg/kubectl/cmd/describe.go +++ b/pkg/kubectl/cmd/describe.go @@ -20,6 +20,7 @@ import ( "fmt" "io" + "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" "github.com/spf13/cobra" ) @@ -36,7 +37,7 @@ given resource.`, checkErr(err) mapper, _ := f.Object(cmd) - mapping, namespace, name := ResourceFromArgs(cmd, args, mapper, cmdNamespace) + mapping, namespace, name := util.ResourceFromArgs(cmd, args, mapper, cmdNamespace) describer, err := f.Describer(cmd, mapping) checkErr(err) diff --git a/pkg/kubectl/cmd/expose.go b/pkg/kubectl/cmd/expose.go index 956da11f3ba..d4ae9d268f0 100644 --- a/pkg/kubectl/cmd/expose.go +++ b/pkg/kubectl/cmd/expose.go @@ -22,6 +22,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" + "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" "github.com/spf13/cobra" ) @@ -51,27 +52,28 @@ $ kubectl expose streamer --port=4100 --protocol=udp --service-name=video-stream client, err := f.Client(cmd) checkErr(err) - generatorName := GetFlagString(cmd, "generator") + generatorName := util.GetFlagString(cmd, "generator") + generator, found := kubectl.Generators[generatorName] if !found { usageError(cmd, fmt.Sprintf("Generator: %s not found.", generator)) } - if GetFlagInt(cmd, "port") < 1 { + if util.GetFlagInt(cmd, "port") < 1 { usageError(cmd, "--port is required and must be a positive integer.") } names := generator.ParamNames() params := kubectl.MakeParams(cmd, names) - if len(GetFlagString(cmd, "service-name")) == 0 { + if len(util.GetFlagString(cmd, "service-name")) == 0 { params["name"] = args[0] } else { - params["name"] = GetFlagString(cmd, "service-name") + params["name"] = util.GetFlagString(cmd, "service-name") } if _, found := params["selector"]; !found { rc, err := client.ReplicationControllers(namespace).Get(args[0]) checkErr(err) params["selector"] = kubectl.MakeLabels(rc.Spec.Selector) } - if GetFlagBool(cmd, "create-external-load-balancer") { + if util.GetFlagBool(cmd, "create-external-load-balancer") { params["create-external-load-balancer"] = "true" } @@ -81,22 +83,22 @@ $ kubectl expose streamer --port=4100 --protocol=udp --service-name=video-stream service, err := generator.Generate(params) checkErr(err) - inline := GetFlagString(cmd, "overrides") + inline := util.GetFlagString(cmd, "overrides") if len(inline) > 0 { - Merge(service, inline, "Service") + util.Merge(service, inline, "Service") } // TODO: extract this flag to a central location, when such a location exists. - if !GetFlagBool(cmd, "dry-run") { + if !util.GetFlagBool(cmd, "dry-run") { service, err = client.Services(namespace).Create(service.(*api.Service)) checkErr(err) } - err = PrintObject(cmd, service, f, out) + err = f.PrintObject(cmd, service, out) checkErr(err) }, } - AddPrinterFlags(cmd) + util.AddPrinterFlags(cmd) cmd.Flags().String("generator", "service/v1", "The name of the api generator that you want to use. Default 'service/v1'") cmd.Flags().String("protocol", "TCP", "The network protocol for the service you want to be created. Default 'tcp'") cmd.Flags().Int("port", -1, "The port that the service should serve on. Required.") diff --git a/pkg/kubectl/cmd/get.go b/pkg/kubectl/cmd/get.go index 273fc1d8688..25977c31ab4 100644 --- a/pkg/kubectl/cmd/get.go +++ b/pkg/kubectl/cmd/get.go @@ -23,6 +23,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" + "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource" "github.com/GoogleCloudPlatform/kubernetes/pkg/watch" @@ -59,7 +60,7 @@ Examples: RunGet(f, out, cmd, args) }, } - AddPrinterFlags(cmd) + util.AddPrinterFlags(cmd) cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on") cmd.Flags().BoolP("watch", "w", false, "After listing/getting the requested object, watch for changes.") cmd.Flags().Bool("watch-only", false, "Watch for changes to the requested object(s), without listing/getting first.") @@ -70,16 +71,16 @@ Examples: // TODO: convert all direct flag accessors to a struct and pass that instead of cmd // TODO: return an error instead of using glog.Fatal and checkErr func RunGet(f *Factory, out io.Writer, cmd *cobra.Command, args []string) { - selector := GetFlagString(cmd, "selector") + selector := util.GetFlagString(cmd, "selector") mapper, typer := f.Object(cmd) cmdNamespace, err := f.DefaultNamespace(cmd) checkErr(err) // handle watch separately since we cannot watch multiple resource types - isWatch, isWatchOnly := GetFlagBool(cmd, "watch"), GetFlagBool(cmd, "watch-only") + isWatch, isWatchOnly := util.GetFlagBool(cmd, "watch"), util.GetFlagBool(cmd, "watch-only") if isWatch || isWatchOnly { - r := resource.NewBuilder(mapper, typer, ClientMapperForCommand(cmd, f)). + r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand(cmd)). NamespaceParam(cmdNamespace).DefaultNamespace(). SelectorParam(selector). ResourceTypeOrNameArgs(args...). @@ -89,7 +90,7 @@ func RunGet(f *Factory, out io.Writer, cmd *cobra.Command, args []string) { mapping, err := r.ResourceMapping() checkErr(err) - printer, err := PrinterForMapping(f, cmd, mapping) + printer, err := f.PrinterForMapping(cmd, mapping) checkErr(err) obj, err := r.Object() @@ -115,12 +116,12 @@ func RunGet(f *Factory, out io.Writer, cmd *cobra.Command, args []string) { return } - b := resource.NewBuilder(mapper, typer, ClientMapperForCommand(cmd, f)). + b := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand(cmd)). NamespaceParam(cmdNamespace).DefaultNamespace(). SelectorParam(selector). ResourceTypeOrNameArgs(args...). Latest() - printer, generic, err := PrinterForCommand(cmd) + printer, generic, err := util.PrinterForCommand(cmd) checkErr(err) if generic { @@ -129,7 +130,7 @@ func RunGet(f *Factory, out io.Writer, cmd *cobra.Command, args []string) { defaultVersion := clientConfig.Version // the outermost object will be converted to the output-version - version := outputVersion(cmd, defaultVersion) + version := util.OutputVersion(cmd, defaultVersion) if len(version) == 0 { // TODO: add a new ResourceBuilder mode for Object() that attempts to ensure the objects // are in the appropriate version if one exists (and if not, use the best effort). @@ -149,7 +150,7 @@ func RunGet(f *Factory, out io.Writer, cmd *cobra.Command, args []string) { // use the default printer for each object err = b.Do().Visit(func(r *resource.Info) error { - printer, err := PrinterForMapping(f, cmd, r.Mapping) + printer, err := f.PrinterForMapping(cmd, r.Mapping) if err != nil { return err } diff --git a/pkg/kubectl/cmd/log.go b/pkg/kubectl/cmd/log.go index f1067172b5d..7a8f768ce34 100644 --- a/pkg/kubectl/cmd/log.go +++ b/pkg/kubectl/cmd/log.go @@ -20,6 +20,7 @@ import ( "io" "strconv" + "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" "github.com/spf13/cobra" ) @@ -66,7 +67,7 @@ Examples: } follow := false - if GetFlagBool(cmd, "follow") { + if util.GetFlagBool(cmd, "follow") { follow = true } diff --git a/pkg/kubectl/cmd/printing_test.go b/pkg/kubectl/cmd/printing_test.go deleted file mode 100644 index 9dc8d06d99b..00000000000 --- a/pkg/kubectl/cmd/printing_test.go +++ /dev/null @@ -1,67 +0,0 @@ -/* -Copyright 2014 Google Inc. All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package cmd_test - -import ( - "fmt" - "os" - - "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - "github.com/GoogleCloudPlatform/kubernetes/pkg/client" - "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" - . "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd" -) - -func ExamplePrintReplicationController() { - f, tf, codec := NewAPIFactory() - tf.Printer = kubectl.NewHumanReadablePrinter(false) - tf.Client = &client.FakeRESTClient{ - Codec: codec, - Client: nil, - } - cmd := f.NewCmdRunContainer(os.Stdout) - ctrl := &api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"foo": "bar"}, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 1, - Selector: map[string]string{"foo": "bar"}, - Template: &api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"foo": "bar"}, - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "foo", - Image: "someimage", - }, - }, - }, - }, - }, - } - err := PrintObject(cmd, ctrl, f, os.Stdout) - if err != nil { - fmt.Printf("Unexpected error: %v", err) - } - // Output: - // CONTROLLER CONTAINER(S) IMAGE(S) SELECTOR REPLICAS - // foo foo someimage foo=bar 1 -} diff --git a/pkg/kubectl/cmd/proxy.go b/pkg/kubectl/cmd/proxy.go index 508c01b5a72..4896be6f5f8 100644 --- a/pkg/kubectl/cmd/proxy.go +++ b/pkg/kubectl/cmd/proxy.go @@ -21,6 +21,7 @@ import ( "strings" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" + "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" "github.com/golang/glog" "github.com/spf13/cobra" ) @@ -31,17 +32,17 @@ func (f *Factory) NewCmdProxy(out io.Writer) *cobra.Command { Short: "Run a proxy to the Kubernetes API server", Long: `Run a proxy to the Kubernetes API server.`, Run: func(cmd *cobra.Command, args []string) { - port := GetFlagInt(cmd, "port") + port := util.GetFlagInt(cmd, "port") glog.Infof("Starting to serve on localhost:%d", port) clientConfig, err := f.ClientConfig(cmd) checkErr(err) - staticPrefix := GetFlagString(cmd, "www-prefix") + staticPrefix := util.GetFlagString(cmd, "www-prefix") if !strings.HasSuffix(staticPrefix, "/") { staticPrefix += "/" } - server, err := kubectl.NewProxyServer(GetFlagString(cmd, "www"), staticPrefix, clientConfig) + server, err := kubectl.NewProxyServer(util.GetFlagString(cmd, "www"), staticPrefix, clientConfig) checkErr(err) glog.Fatal(server.Serve(port)) }, diff --git a/pkg/kubectl/cmd/resize.go b/pkg/kubectl/cmd/resize.go index 2a547663427..d04c564498a 100644 --- a/pkg/kubectl/cmd/resize.go +++ b/pkg/kubectl/cmd/resize.go @@ -21,6 +21,7 @@ import ( "io" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" + "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" "github.com/spf13/cobra" ) @@ -44,7 +45,7 @@ Examples: $ kubectl resize --current-replicas=2 --replicas=3 replicationcontrollers foo `, Run: func(cmd *cobra.Command, args []string) { - count := GetFlagInt(cmd, "replicas") + count := util.GetFlagInt(cmd, "replicas") if len(args) != 2 || count < 0 { usageError(cmd, "--replicas= ") } @@ -53,13 +54,13 @@ Examples: checkErr(err) mapper, _ := f.Object(cmd) - mapping, namespace, name := ResourceFromArgs(cmd, args, mapper, cmdNamespace) + mapping, namespace, name := util.ResourceFromArgs(cmd, args, mapper, cmdNamespace) resizer, err := f.Resizer(cmd, mapping) checkErr(err) - resourceVersion := GetFlagString(cmd, "resource-version") - currentSize := GetFlagInt(cmd, "current-replicas") + resourceVersion := util.GetFlagString(cmd, "resource-version") + currentSize := util.GetFlagInt(cmd, "current-replicas") s, err := resizer.Resize(namespace, name, &kubectl.ResizePrecondition{currentSize, resourceVersion}, uint(count)) checkErr(err) fmt.Fprintf(out, "%s\n", s) diff --git a/pkg/kubectl/cmd/rollingupdate.go b/pkg/kubectl/cmd/rollingupdate.go index 4cf57c565ba..8abcba5b801 100644 --- a/pkg/kubectl/cmd/rollingupdate.go +++ b/pkg/kubectl/cmd/rollingupdate.go @@ -22,6 +22,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" + "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" "github.com/spf13/cobra" ) @@ -48,13 +49,13 @@ $ kubectl rollingupdate frontend-v1 -f frontend-v2.json $ cat frontend-v2.json | kubectl rollingupdate frontend-v1 -f - `, Run: func(cmd *cobra.Command, args []string) { - filename := GetFlagString(cmd, "filename") + filename := util.GetFlagString(cmd, "filename") if len(filename) == 0 { usageError(cmd, "Must specify filename for new controller") } - period := GetFlagDuration(cmd, "update-period") - interval := GetFlagDuration(cmd, "poll-interval") - timeout := GetFlagDuration(cmd, "timeout") + period := util.GetFlagDuration(cmd, "update-period") + interval := util.GetFlagDuration(cmd, "poll-interval") + timeout := util.GetFlagDuration(cmd, "timeout") if len(args) != 1 { usageError(cmd, "Must specify the controller to update") } @@ -67,7 +68,7 @@ $ cat frontend-v2.json | kubectl rollingupdate frontend-v1 -f - cmdApiVersion := clientConfig.Version mapper, typer := f.Object(cmd) - mapping, namespace, newName, data := ResourceFromFile(filename, typer, mapper, schema, cmdApiVersion) + mapping, namespace, newName, data := util.ResourceFromFile(filename, typer, mapper, schema, cmdApiVersion) if mapping.Kind != "ReplicationController" { usageError(cmd, "%s does not specify a valid ReplicationController", filename) } @@ -78,7 +79,7 @@ $ cat frontend-v2.json | kubectl rollingupdate frontend-v1 -f - cmdNamespace, err := f.DefaultNamespace(cmd) checkErr(err) - err = CompareNamespace(cmdNamespace, namespace) + err = util.CompareNamespace(cmdNamespace, namespace) checkErr(err) client, err := f.Client(cmd) diff --git a/pkg/kubectl/cmd/run.go b/pkg/kubectl/cmd/run.go index dc96df3959d..3b6bcab5adf 100644 --- a/pkg/kubectl/cmd/run.go +++ b/pkg/kubectl/cmd/run.go @@ -22,6 +22,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" + "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" "github.com/spf13/cobra" ) @@ -55,7 +56,7 @@ Examples: client, err := f.Client(cmd) checkErr(err) - generatorName := GetFlagString(cmd, "generator") + generatorName := util.GetFlagString(cmd, "generator") generator, found := kubectl.Generators[generatorName] if !found { usageError(cmd, fmt.Sprintf("Generator: %s not found.", generator)) @@ -70,22 +71,22 @@ Examples: controller, err := generator.Generate(params) checkErr(err) - inline := GetFlagString(cmd, "overrides") + inline := util.GetFlagString(cmd, "overrides") if len(inline) > 0 { - Merge(controller, inline, "ReplicationController") + util.Merge(controller, inline, "ReplicationController") } // TODO: extract this flag to a central location, when such a location exists. - if !GetFlagBool(cmd, "dry-run") { + if !util.GetFlagBool(cmd, "dry-run") { controller, err = client.ReplicationControllers(namespace).Create(controller.(*api.ReplicationController)) checkErr(err) } - err = PrintObject(cmd, controller, f, out) + err = f.PrintObject(cmd, controller, out) checkErr(err) }, } - AddPrinterFlags(cmd) + util.AddPrinterFlags(cmd) cmd.Flags().String("generator", "run-container/v1", "The name of the api generator that you want to use. Default 'run-container-controller/v1'") cmd.Flags().String("image", "", "The image for the container you wish to run.") cmd.Flags().IntP("replicas", "r", 1, "Number of replicas to create for this container. Default 1") diff --git a/pkg/kubectl/cmd/stop.go b/pkg/kubectl/cmd/stop.go index 17e2782f3d0..3da95bf67a3 100644 --- a/pkg/kubectl/cmd/stop.go +++ b/pkg/kubectl/cmd/stop.go @@ -20,6 +20,7 @@ import ( "fmt" "io" + "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" "github.com/spf13/cobra" ) @@ -42,7 +43,7 @@ Examples: } cmdNamespace, err := f.DefaultNamespace(cmd) mapper, _ := f.Object(cmd) - mapping, namespace, name := ResourceFromArgs(cmd, args, mapper, cmdNamespace) + mapping, namespace, name := util.ResourceFromArgs(cmd, args, mapper, cmdNamespace) reaper, err := f.Reaper(cmd, mapping) checkErr(err) diff --git a/pkg/kubectl/cmd/update.go b/pkg/kubectl/cmd/update.go index 69ebb5b19a4..83c239355f0 100644 --- a/pkg/kubectl/cmd/update.go +++ b/pkg/kubectl/cmd/update.go @@ -20,6 +20,7 @@ import ( "fmt" "io" + cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/spf13/cobra" @@ -53,14 +54,14 @@ Examples: checkErr(err) mapper, typer := f.Object(cmd) - r := resource.NewBuilder(mapper, typer, ClientMapperForCommand(cmd, f)). + r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand(cmd)). ContinueOnError(). NamespaceParam(cmdNamespace).RequireNamespace(). FilenameParam(flags.Filenames...). Flatten(). Do() - patch := GetFlagString(cmd, "patch") + patch := cmdutil.GetFlagString(cmd, "patch") if len(flags.Filenames) == 0 && len(patch) == 0 { usageError(cmd, "Must specify --filename or --patch to update") } @@ -102,7 +103,7 @@ func updateWithPatch(cmd *cobra.Command, args []string, f *Factory, patch string checkErr(err) mapper, _ := f.Object(cmd) - mapping, namespace, name := ResourceFromArgs(cmd, args, mapper, cmdNamespace) + mapping, namespace, name := cmdutil.ResourceFromArgs(cmd, args, mapper, cmdNamespace) client, err := f.RESTClient(cmd, mapping) checkErr(err) @@ -110,7 +111,7 @@ func updateWithPatch(cmd *cobra.Command, args []string, f *Factory, patch string obj, err := helper.Get(namespace, name) checkErr(err) - Merge(obj, patch, mapping.Kind) + cmdutil.Merge(obj, patch, mapping.Kind) data, err := helper.Codec.Encode(obj) checkErr(err) diff --git a/pkg/kubectl/cmd/helpers.go b/pkg/kubectl/cmd/util/helpers.go similarity index 95% rename from pkg/kubectl/cmd/helpers.go rename to pkg/kubectl/cmd/util/helpers.go index aa3c7053cdc..d3001c059d4 100644 --- a/pkg/kubectl/cmd/helpers.go +++ b/pkg/kubectl/cmd/util/helpers.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package cmd +package util import ( "encoding/json" @@ -34,6 +34,18 @@ import ( "github.com/spf13/cobra" ) +func checkErr(err error) { + if err != nil { + glog.FatalDepth(1, err) + } +} + +func usageError(cmd *cobra.Command, format string, args ...interface{}) { + glog.Errorf(format, args...) + glog.Errorf("See '%s -h' for help.", cmd.CommandPath()) + os.Exit(1) +} + func GetFlagString(cmd *cobra.Command, flag string) string { f := cmd.Flags().Lookup(flag) if f == nil { diff --git a/pkg/kubectl/cmd/helpers_test.go b/pkg/kubectl/cmd/util/helpers_test.go similarity index 99% rename from pkg/kubectl/cmd/helpers_test.go rename to pkg/kubectl/cmd/util/helpers_test.go index ed0ec504ea3..32673373c74 100644 --- a/pkg/kubectl/cmd/helpers_test.go +++ b/pkg/kubectl/cmd/util/helpers_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package cmd +package util import ( "reflect" diff --git a/pkg/kubectl/cmd/printing.go b/pkg/kubectl/cmd/util/printing.go similarity index 50% rename from pkg/kubectl/cmd/printing.go rename to pkg/kubectl/cmd/util/printing.go index 3f3b5ad5bae..284b5442c00 100644 --- a/pkg/kubectl/cmd/printing.go +++ b/pkg/kubectl/cmd/util/printing.go @@ -14,16 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -package cmd +package util import ( - "fmt" - "io" - - "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - "github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" - "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/spf13/cobra" ) @@ -35,28 +29,8 @@ func AddPrinterFlags(cmd *cobra.Command) { cmd.Flags().StringP("template", "t", "", "Template string or path to template file to use when -o=template or -o=templatefile.") } -// PrintObject prints an api object given command line flags to modify the output format -func PrintObject(cmd *cobra.Command, obj runtime.Object, f *Factory, out io.Writer) error { - mapper, _ := f.Object(cmd) - _, kind, err := api.Scheme.ObjectVersionAndKind(obj) - if err != nil { - return err - } - - mapping, err := mapper.RESTMapping(kind) - if err != nil { - return err - } - - printer, err := PrinterForMapping(f, cmd, mapping) - if err != nil { - return err - } - return printer.PrintObj(obj, out) -} - -// outputVersion returns the preferred output version for generic content (JSON, YAML, or templates) -func outputVersion(cmd *cobra.Command, defaultVersion string) string { +// OutputVersion returns the preferred output version for generic content (JSON, YAML, or templates) +func OutputVersion(cmd *cobra.Command, defaultVersion string) string { outputVersion := GetFlagString(cmd, "output-version") if len(outputVersion) == 0 { outputVersion = defaultVersion @@ -75,32 +49,3 @@ func PrinterForCommand(cmd *cobra.Command) (kubectl.ResourcePrinter, bool, error return kubectl.GetPrinter(outputFormat, templateFile) } - -// PrinterForMapping returns a printer suitable for displaying the provided resource type. -// Requires that printer flags have been added to cmd (see AddPrinterFlags). -func PrinterForMapping(f *Factory, cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.ResourcePrinter, error) { - printer, ok, err := PrinterForCommand(cmd) - if err != nil { - return nil, err - } - if ok { - clientConfig, err := f.ClientConfig(cmd) - checkErr(err) - defaultVersion := clientConfig.Version - - version := outputVersion(cmd, defaultVersion) - if len(version) == 0 { - version = mapping.APIVersion - } - if len(version) == 0 { - return nil, fmt.Errorf("you must specify an output-version when using this output format") - } - printer = kubectl.NewVersionedPrinter(printer, mapping.ObjectConvertor, version) - } else { - printer, err = f.Printer(cmd, mapping, GetFlagBool(cmd, "no-headers")) - if err != nil { - return nil, err - } - } - return printer, nil -} diff --git a/pkg/kubectl/cmd/resource.go b/pkg/kubectl/cmd/util/resource.go similarity index 99% rename from pkg/kubectl/cmd/resource.go rename to pkg/kubectl/cmd/util/resource.go index ab440996786..a4f4ca9310b 100644 --- a/pkg/kubectl/cmd/resource.go +++ b/pkg/kubectl/cmd/util/resource.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package cmd +package util import ( "fmt" diff --git a/pkg/kubectl/cmd/version.go b/pkg/kubectl/cmd/version.go index 5e434a58e16..0a8282ec5b3 100644 --- a/pkg/kubectl/cmd/version.go +++ b/pkg/kubectl/cmd/version.go @@ -22,6 +22,7 @@ import ( "github.com/spf13/cobra" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" + "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" ) func (f *Factory) NewCmdVersion(out io.Writer) *cobra.Command { @@ -29,7 +30,7 @@ func (f *Factory) NewCmdVersion(out io.Writer) *cobra.Command { Use: "version", Short: "Print version of client and server", Run: func(cmd *cobra.Command, args []string) { - if GetFlagBool(cmd, "client") { + if util.GetFlagBool(cmd, "client") { kubectl.GetClientVersion(out) return } From dad85ef4f1f493d5437fad1702acce508bed5664 Mon Sep 17 00:00:00 2001 From: Zach Loafman Date: Thu, 5 Feb 2015 10:00:18 -0800 Subject: [PATCH 39/50] Add more logging --- hack/jenkins/e2e.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hack/jenkins/e2e.sh b/hack/jenkins/e2e.sh index 1a0dde2b939..29444c418da 100755 --- a/hack/jenkins/e2e.sh +++ b/hack/jenkins/e2e.sh @@ -80,6 +80,9 @@ fi export KUBE_CONFIG_FILE="config-test.sh" cluster/kube-down.sh || true cluster/kube-up.sh || { echo "-- kube-up failed --"; exit 1; } +echo +echo "-- cluster created at $(date -Is) --" +echo cluster/kubectl.sh version || { echo "-- kubectl failed --"; exit 1; } hack/ginkgo-e2e.sh --report_dir=${WORKSPACE} || { echo "-- tests failed --"; exit 1; } cluster/kube-down.sh || { echo "-- kube-down failed --"; exit 1; } From 70237e9cc608be1c3aece3ac7ed8b421cf21cb53 Mon Sep 17 00:00:00 2001 From: Robert Rati Date: Thu, 5 Feb 2015 12:53:03 -0500 Subject: [PATCH 40/50] Changed to return in the success case testing testContext instead of using an else statement. #4162 --- test/e2e/service.go | 228 ++++++++++++++++++++++---------------------- 1 file changed, 114 insertions(+), 114 deletions(-) diff --git a/test/e2e/service.go b/test/e2e/service.go index 781fbfdea37..c993e86dc43 100644 --- a/test/e2e/service.go +++ b/test/e2e/service.go @@ -38,121 +38,121 @@ var _ = Describe("Services", func() { It("should provide DNS for the cluster", func() { if testContext.provider == "vagrant" { By("Skipping test which is broken for vagrant (See https://github.com/GoogleCloudPlatform/kubernetes/issues/3580)") - } else { - podClient := c.Pods(api.NamespaceDefault) - - //TODO: Wait for skyDNS - - // All the names we need to be able to resolve. - namesToResolve := []string{ - "kubernetes-ro", - "kubernetes-ro.default", - "kubernetes-ro.default.kubernetes.local", - "google.com", - } - - probeCmd := "for i in `seq 1 600`; do " - for _, name := range namesToResolve { - probeCmd += fmt.Sprintf("wget -O /dev/null %s && echo OK > /results/%s;", name, name) - } - probeCmd += "sleep 1; done" - - // Run a pod which probes DNS and exposes the results by HTTP. - By("creating a pod to probe DNS") - pod := &api.Pod{ - TypeMeta: api.TypeMeta{ - Kind: "Pod", - APIVersion: "v1beta1", - }, - ObjectMeta: api.ObjectMeta{ - Name: "dns-test-" + string(util.NewUUID()), - }, - Spec: api.PodSpec{ - Volumes: []api.Volume{ - { - Name: "results", - Source: api.VolumeSource{ - EmptyDir: &api.EmptyDir{}, - }, - }, - }, - Containers: []api.Container{ - { - Name: "webserver", - Image: "kubernetes/test-webserver", - VolumeMounts: []api.VolumeMount{ - { - Name: "results", - MountPath: "/results", - }, - }, - }, - { - Name: "pinger", - Image: "busybox", - Command: []string{"sh", "-c", probeCmd}, - VolumeMounts: []api.VolumeMount{ - { - Name: "results", - MountPath: "/results", - }, - }, - }, - }, - }, - } - - By("submitting the pod to kuberenetes") - _, err := podClient.Create(pod) - if err != nil { - Fail(fmt.Sprintf("Failed to create %s pod: %v", pod.Name, err)) - } - defer func() { - By("deleting the pod") - defer GinkgoRecover() - podClient.Delete(pod.Name) - }() - - - By("waiting for the pod to start running") - waitForPodRunning(c, pod.Name) - - By("retrieving the pod") - pod, err = podClient.Get(pod.Name) - if err != nil { - Fail(fmt.Sprintf("Failed to get pod %s: %v", pod.Name, err)) - } - - // Try to find results for each expected name. - By("looking for the results for each expected name") - var failed []string - for try := 1; try < 100; try++ { - failed = []string{} - for _, name := range namesToResolve { - _, err := c.Get(). - Prefix("proxy"). - Resource("pods"). - Namespace("default"). - Name(pod.Name). - Suffix("results", name). - Do().Raw() - if err != nil { - failed = append(failed, name) - fmt.Printf("Lookup using %s for %s failed: %v\n", pod.Name, name, err) - } - } - if len(failed) == 0 { - break - } - fmt.Printf("lookups using %s failed for: %v\n", pod.Name, failed) - time.Sleep(10 * time.Second) - } - Expect(len(failed)).To(Equal(0)) - - // TODO: probe from the host, too. - - fmt.Printf("DNS probes using %s succeeded\n", pod.Name) + return } + + podClient := c.Pods(api.NamespaceDefault) + + //TODO: Wait for skyDNS + + // All the names we need to be able to resolve. + namesToResolve := []string{ + "kubernetes-ro", + "kubernetes-ro.default", + "kubernetes-ro.default.kubernetes.local", + "google.com", + } + + probeCmd := "for i in `seq 1 600`; do " + for _, name := range namesToResolve { + probeCmd += fmt.Sprintf("wget -O /dev/null %s && echo OK > /results/%s;", name, name) + } + probeCmd += "sleep 1; done" + + // Run a pod which probes DNS and exposes the results by HTTP. + By("creating a pod to probe DNS") + pod := &api.Pod{ + TypeMeta: api.TypeMeta{ + Kind: "Pod", + APIVersion: "v1beta1", + }, + ObjectMeta: api.ObjectMeta{ + Name: "dns-test-" + string(util.NewUUID()), + }, + Spec: api.PodSpec{ + Volumes: []api.Volume{ + { + Name: "results", + Source: api.VolumeSource{ + EmptyDir: &api.EmptyDir{}, + }, + }, + }, + Containers: []api.Container{ + { + Name: "webserver", + Image: "kubernetes/test-webserver", + VolumeMounts: []api.VolumeMount{ + { + Name: "results", + MountPath: "/results", + }, + }, + }, + { + Name: "pinger", + Image: "busybox", + Command: []string{"sh", "-c", probeCmd}, + VolumeMounts: []api.VolumeMount{ + { + Name: "results", + MountPath: "/results", + }, + }, + }, + }, + }, + } + + By("submitting the pod to kuberenetes") + _, err := podClient.Create(pod) + if err != nil { + Fail(fmt.Sprintf("Failed to create %s pod: %v", pod.Name, err)) + } + defer func() { + By("deleting the pod") + defer GinkgoRecover() + podClient.Delete(pod.Name) + }() + + By("waiting for the pod to start running") + waitForPodRunning(c, pod.Name) + + By("retrieving the pod") + pod, err = podClient.Get(pod.Name) + if err != nil { + Fail(fmt.Sprintf("Failed to get pod %s: %v", pod.Name, err)) + } + + // Try to find results for each expected name. + By("looking for the results for each expected name") + var failed []string + for try := 1; try < 100; try++ { + failed = []string{} + for _, name := range namesToResolve { + _, err := c.Get(). + Prefix("proxy"). + Resource("pods"). + Namespace("default"). + Name(pod.Name). + Suffix("results", name). + Do().Raw() + if err != nil { + failed = append(failed, name) + fmt.Printf("Lookup using %s for %s failed: %v\n", pod.Name, name, err) + } + } + if len(failed) == 0 { + break + } + fmt.Printf("lookups using %s failed for: %v\n", pod.Name, failed) + time.Sleep(10 * time.Second) + } + Expect(len(failed)).To(Equal(0)) + + // TODO: probe from the host, too. + + fmt.Printf("DNS probes using %s succeeded\n", pod.Name) }) It("should provide RW and RO services", func() { From c15595fdf727bce40fe563d364b8b2dd18724347 Mon Sep 17 00:00:00 2001 From: Zach Loafman Date: Thu, 5 Feb 2015 10:23:19 -0800 Subject: [PATCH 41/50] Revert jenkins/e2e.sh back to 606b517a, minus -tap --- hack/jenkins/e2e.sh | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/hack/jenkins/e2e.sh b/hack/jenkins/e2e.sh index 29444c418da..358c68ec26b 100755 --- a/hack/jenkins/e2e.sh +++ b/hack/jenkins/e2e.sh @@ -77,12 +77,11 @@ if [[ ! -z ${E2E_SET_CLUSTER_API_VERSION:-} ]]; then export CLUSTER_API_VERSION=$(echo ${GITHASH} | cut -c 2-) fi -export KUBE_CONFIG_FILE="config-test.sh" -cluster/kube-down.sh || true -cluster/kube-up.sh || { echo "-- kube-up failed --"; exit 1; } -echo -echo "-- cluster created at $(date -Is) --" -echo -cluster/kubectl.sh version || { echo "-- kubectl failed --"; exit 1; } -hack/ginkgo-e2e.sh --report_dir=${WORKSPACE} || { echo "-- tests failed --"; exit 1; } -cluster/kube-down.sh || { echo "-- kube-down failed --"; exit 1; } +# Have cmd/e2e run by goe2e.sh generate JUnit report in ${WORKSPACE}/junit*.xml +export E2E_REPORT_DIR=${WORKSPACE} + +go run ./hack/e2e.go ${E2E_OPT} -v --down +go run ./hack/e2e.go ${E2E_OPT} -v --up +go run ./hack/e2e.go -v --ctl="version --match-server-version=false" +go run ./hack/e2e.go ${E2E_OPT} -v --test +go run ./hack/e2e.go ${E2E_OPT} -v --down From 9d9bca995854706719e4ac5e6d940a68eb77ae78 Mon Sep 17 00:00:00 2001 From: Wojciech Tyczynski Date: Thu, 5 Feb 2015 19:37:20 +0100 Subject: [PATCH 42/50] kubectl doesn't fail silently with no k8s master --- cluster/kubectl.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cluster/kubectl.sh b/cluster/kubectl.sh index 9642c22e426..9a4ca11fdea 100755 --- a/cluster/kubectl.sh +++ b/cluster/kubectl.sh @@ -123,7 +123,7 @@ elif [[ "${KUBERNETES_PROVIDER}" == "gke" ]]; then ) fi -detect-master &> /dev/null +detect-master > /dev/null if [[ -n "${KUBE_MASTER_IP-}" && -z "${KUBERNETES_MASTER-}" ]]; then export KUBERNETES_MASTER=https://${KUBE_MASTER_IP} fi From 5a702ad123b79f266f72f4ac437e462a66c07c06 Mon Sep 17 00:00:00 2001 From: Jeff Lowdermilk Date: Wed, 4 Feb 2015 17:47:52 -0800 Subject: [PATCH 43/50] Allow --output formatting of kubectl config view, make merge default --- docs/kubectl.md | 16 +++++++++--- pkg/kubectl/cmd/config/view.go | 48 +++++++++++++++++++--------------- 2 files changed, 40 insertions(+), 24 deletions(-) diff --git a/docs/kubectl.md b/docs/kubectl.md index 026e115c81a..992cf657a41 100644 --- a/docs/kubectl.md +++ b/docs/kubectl.md @@ -338,7 +338,7 @@ Usage: kubectl config [command] Available Commands: - view displays the specified .kubeconfig file or a merged result + view displays merged .kubeconfig settings or a specified .kubeconfig file. set-cluster name [--server=server] [--certificate-authority=path/to/certficate/authority] [--api-version=apiversion] [--insecure-skip-tls-verify=true] Sets a cluster entry in .kubeconfig set-credentials name [--auth-path=path/to/auth/file] [--client-certificate=path/to/certficate/file] [--client-key=path/to/key/file] [--token=bearer_token_string] Sets a user entry in .kubeconfig set-context name [--cluster=cluster-nickname] [--user=user-nickname] [--namespace=namespace] Sets a context entry in .kubeconfig @@ -395,7 +395,13 @@ Use "kubectl help [command]" for more information about that command. ``` #### config view -displays the specified .kubeconfig file or a merged result +displays merged .kubeconfig settings or a specified .kubeconfig file. +Examples: + // Show merged .kubeconfig settings. + $ kubectl config view + + // Show only local ./.kubeconfig settings + $ kubectl config view --local Usage: ``` @@ -421,10 +427,14 @@ Usage: --log_flush_frequency=5s: Maximum number of seconds between log flushes --logtostderr=true: log to standard error instead of files --match-server-version=false: Require server version to match client version - --merge=false: merge together the full hierarchy of .kubeconfig files + --merge=true: merge together the full hierarchy of .kubeconfig files --namespace="": If present, the namespace scope for this CLI request. + --no-headers=false: When using the default output, don't print headers + -o, --output="": Output format: json|yaml|template|templatefile + --output-version="": Output the formatted object with the given version (default api-version) -s, --server="": The address of the Kubernetes API server --stderrthreshold=2: logs at or above this threshold go to stderr + -t, --template="": Template string or path to template file to use when -o=template or -o=templatefile. --token="": Bearer token for authentication to the API server. --user="": The name of the kubeconfig user to use --v=0: log level for V logs diff --git a/pkg/kubectl/cmd/config/view.go b/pkg/kubectl/cmd/config/view.go index 4f8c83fc50e..738746d9896 100644 --- a/pkg/kubectl/cmd/config/view.go +++ b/pkg/kubectl/cmd/config/view.go @@ -21,11 +21,12 @@ import ( "io" "os" - "github.com/ghodss/yaml" + "github.com/golang/glog" "github.com/spf13/cobra" "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" ) type viewOptions struct { @@ -38,40 +39,45 @@ func NewCmdConfigView(out io.Writer, pathOptions *pathOptions) *cobra.Command { cmd := &cobra.Command{ Use: "view", - Short: "displays the specified .kubeconfig file or a merged result", - Long: `displays the specified .kubeconfig file or a merged result`, + Short: "displays merged .kubeconfig settings or a specified .kubeconfig file.", + Long: `displays merged .kubeconfig settings or a specified .kubeconfig file. +Examples: + // Show merged .kubeconfig settings. + $ kubectl config view + + // Show only local ./.kubeconfig settings + $ kubectl config view --local`, Run: func(cmd *cobra.Command, args []string) { - err := options.run() + printer, _, err := util.PrinterForCommand(cmd) if err != nil { - fmt.Printf("%v\n", err) + glog.FatalDepth(1, err) + } + config, err := options.loadConfig() + if err != nil { + glog.FatalDepth(1, err) + } + err = printer.PrintObj(config, out) + if err != nil { + glog.FatalDepth(1, err) } }, } - cmd.Flags().BoolVar(&options.merge, "merge", false, "merge together the full hierarchy of .kubeconfig files") - + util.AddPrinterFlags(cmd) + // Default to yaml + cmd.Flags().Set("output", "yaml") + cmd.Flags().BoolVar(&options.merge, "merge", true, "merge together the full hierarchy of .kubeconfig files") return cmd } -func (o viewOptions) run() error { +func (o viewOptions) loadConfig() (*clientcmdapi.Config, error) { err := o.validate() if err != nil { - return err + return nil, err } config, _, err := o.getStartingConfig() - if err != nil { - return err - } - - content, err := yaml.Marshal(config) - if err != nil { - return err - } - - fmt.Printf("%v", string(content)) - - return nil + return config, err } func (o viewOptions) validate() error { From 6d0b8ea7a7541f3330d05ad1dd5f1710b92069ca Mon Sep 17 00:00:00 2001 From: Brendan Burns Date: Thu, 5 Feb 2015 09:37:29 -0800 Subject: [PATCH 44/50] Fix a regression where we never cleared out failed nodes. --- pkg/controller/replication_controller.go | 3 ++- pkg/master/pod_cache.go | 10 ++++++++ pkg/master/pod_cache_test.go | 29 ++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/pkg/controller/replication_controller.go b/pkg/controller/replication_controller.go index 2f9c03221b5..04212fafc7d 100644 --- a/pkg/controller/replication_controller.go +++ b/pkg/controller/replication_controller.go @@ -166,7 +166,8 @@ func FilterActivePods(pods []api.Pod) []api.Pod { var result []api.Pod for _, value := range pods { if api.PodSucceeded != value.Status.Phase && - api.PodFailed != value.Status.Phase { + api.PodFailed != value.Status.Phase && + api.PodUnknown != value.Status.Phase { result = append(result, value) } } diff --git a/pkg/master/pod_cache.go b/pkg/master/pod_cache.go index d9d1ed3a614..70cf92da24c 100644 --- a/pkg/master/pod_cache.go +++ b/pkg/master/pod_cache.go @@ -135,6 +135,12 @@ func (p *PodCache) getNodeStatus(name string) (*api.NodeStatus, error) { return &node.Status, nil } +func (p *PodCache) clearNodeStatus() { + p.lock.Lock() + defer p.lock.Unlock() + p.currentNodes = map[objKey]api.NodeStatus{} +} + // TODO: once Host gets moved to spec, this can take a podSpec + metadata instead of an // entire pod? func (p *PodCache) updatePodStatus(pod *api.Pod) error { @@ -221,6 +227,10 @@ func (p *PodCache) GarbageCollectPodStatus() { // calling again, or risk having new info getting clobbered by delayed // old info. func (p *PodCache) UpdateAllContainers() { + // TODO: this is silly, we should pro-actively update the pod status when + // the API server makes changes. + p.clearNodeStatus() + ctx := api.NewContext() pods, err := p.pods.ListPods(ctx, labels.Everything()) if err != nil { diff --git a/pkg/master/pod_cache_test.go b/pkg/master/pod_cache_test.go index b87c985fe30..302f44393d0 100644 --- a/pkg/master/pod_cache_test.go +++ b/pkg/master/pod_cache_test.go @@ -254,6 +254,35 @@ func makeUnhealthyNode(name string) *api.Node { } } +func TestPodUpdateAllContainersClearsNodeStatus(t *testing.T) { + node := makeHealthyNode("machine", "1.2.3.5") + pod1 := makePod(api.NamespaceDefault, "foo", "machine", "bar") + pod2 := makePod(api.NamespaceDefault, "baz", "machine", "qux") + config := podCacheTestConfig{ + kubeletContainerInfo: api.PodStatus{ + Info: api.PodInfo{"bar": api.ContainerStatus{}}}, + nodes: []api.Node{*node}, + pods: []api.Pod{*pod1, *pod2}, + } + cache := config.Construct() + + if len(cache.currentNodes) != 0 { + t.Errorf("unexpected node cache: %v", cache.currentNodes) + } + key := objKey{"", "machine"} + cache.currentNodes[key] = makeUnhealthyNode("machine").Status + + cache.UpdateAllContainers() + + if len(cache.currentNodes) != 1 { + t.Errorf("unexpected empty node cache: %v", cache.currentNodes) + } + + if !reflect.DeepEqual(cache.currentNodes[key], node.Status) { + t.Errorf("unexpected status:\n%#v\nexpected:\n%#v\n", cache.currentNodes[key], node.Status) + } +} + func TestPodUpdateAllContainers(t *testing.T) { pod1 := makePod(api.NamespaceDefault, "foo", "machine", "bar") pod2 := makePod(api.NamespaceDefault, "baz", "machine", "qux") From 49a1984e9815d1187b8fb8a66cd892ac7b3bcef9 Mon Sep 17 00:00:00 2001 From: Zach Loafman Date: Thu, 5 Feb 2015 11:24:45 -0800 Subject: [PATCH 45/50] Breakfix for #4177, not a real solution --- hack/jenkins/e2e.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hack/jenkins/e2e.sh b/hack/jenkins/e2e.sh index 358c68ec26b..37c2a1f1d48 100755 --- a/hack/jenkins/e2e.sh +++ b/hack/jenkins/e2e.sh @@ -83,5 +83,5 @@ export E2E_REPORT_DIR=${WORKSPACE} go run ./hack/e2e.go ${E2E_OPT} -v --down go run ./hack/e2e.go ${E2E_OPT} -v --up go run ./hack/e2e.go -v --ctl="version --match-server-version=false" -go run ./hack/e2e.go ${E2E_OPT} -v --test +go run ./hack/e2e.go ${E2E_OPT} -v --test || echo "Ignored, Jenkins will pass/fail based on test failures" go run ./hack/e2e.go ${E2E_OPT} -v --down From 18c5fa1ae76b557a6dcb9ddd9c0017f0bc0cd221 Mon Sep 17 00:00:00 2001 From: Jerzy Szczepkowski Date: Thu, 5 Feb 2015 20:32:51 +0100 Subject: [PATCH 46/50] Applied comments. --- pkg/constraint/constraint.go | 9 +++++---- pkg/constraint/constraint_test.go | 12 ++++++------ pkg/constraint/ports.go | 12 +++++++----- pkg/registry/etcd/etcd.go | 4 ++-- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/pkg/constraint/constraint.go b/pkg/constraint/constraint.go index 078ca6d59d1..430c1a85cb2 100644 --- a/pkg/constraint/constraint.go +++ b/pkg/constraint/constraint.go @@ -24,9 +24,10 @@ import ( // Allowed returns true if pods is a collection of bound pods // which can run without conflict on a single minion. -func Allowed(pods []api.BoundPod) error { - if PortsConflict(pods) { - return fmt.Errorf("conflicting ports") +func Allowed(pods []api.BoundPod) []error { + errors := []error{} + for _, port := range hostPortsConflict(pods) { + errors = append(errors, fmt.Errorf("host port %v is already in use", port)) } - return nil + return errors } diff --git a/pkg/constraint/constraint_test.go b/pkg/constraint/constraint_test.go index bb6f84fc89d..eae45221270 100644 --- a/pkg/constraint/constraint_test.go +++ b/pkg/constraint/constraint_test.go @@ -41,11 +41,11 @@ func podWithContainers(containers ...api.Container) api.BoundPod { func TestAllowed(t *testing.T) { table := []struct { - err error + err string pods []api.BoundPod }{ { - err: nil, + err: "[]", pods: []api.BoundPod{ podWithContainers( containerWithHostPorts(1, 2, 3), @@ -58,7 +58,7 @@ func TestAllowed(t *testing.T) { }, }, { - err: nil, + err: "[]", pods: []api.BoundPod{ podWithContainers( containerWithHostPorts(0, 0), @@ -71,7 +71,7 @@ func TestAllowed(t *testing.T) { }, }, { - err: fmt.Errorf("conflicting ports"), + err: "[host port 3 is already in use]", pods: []api.BoundPod{ podWithContainers( containerWithHostPorts(3, 3), @@ -79,7 +79,7 @@ func TestAllowed(t *testing.T) { }, }, { - err: fmt.Errorf("conflicting ports"), + err: "[host port 6 is already in use]", pods: []api.BoundPod{ podWithContainers( containerWithHostPorts(6), @@ -92,7 +92,7 @@ func TestAllowed(t *testing.T) { } for _, item := range table { - if e, a := item.err, Allowed(item.pods); e != a && e.Error() != a.Error() { + if e, a := item.err, Allowed(item.pods); e != fmt.Sprintf("%v", a) { t.Errorf("Expected %v, got %v: \n%v\v", e, a, item.pods) } } diff --git a/pkg/constraint/ports.go b/pkg/constraint/ports.go index f5c95f5fb2d..b7398a6ea78 100644 --- a/pkg/constraint/ports.go +++ b/pkg/constraint/ports.go @@ -20,10 +20,12 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api" ) -// PortsConflict returns true iff two containers attempt to expose -// the same host port. -func PortsConflict(pods []api.BoundPod) bool { +// hostPortsConflict returns an array of host ports that at least two +// containers attempt to expose. The array is empty if no such port +// exists. +func hostPortsConflict(pods []api.BoundPod) []int { hostPorts := map[int]struct{}{} + conflictingPorts := []int{} for _, pod := range pods { for _, container := range pod.Spec.Containers { for _, port := range container.Ports { @@ -31,11 +33,11 @@ func PortsConflict(pods []api.BoundPod) bool { continue } if _, exists := hostPorts[port.HostPort]; exists { - return true + conflictingPorts = append(conflictingPorts, port.HostPort) } hostPorts[port.HostPort] = struct{}{} } } } - return false + return conflictingPorts } diff --git a/pkg/registry/etcd/etcd.go b/pkg/registry/etcd/etcd.go index 49c32eff69e..c5114a8320b 100644 --- a/pkg/registry/etcd/etcd.go +++ b/pkg/registry/etcd/etcd.go @@ -214,8 +214,8 @@ func (r *Registry) assignPod(ctx api.Context, podID string, machine string) erro err = r.AtomicUpdate(contKey, &api.BoundPods{}, func(in runtime.Object) (runtime.Object, error) { boundPodList := in.(*api.BoundPods) boundPodList.Items = append(boundPodList.Items, *boundPod) - if e := constraint.Allowed(boundPodList.Items); e != nil { - return nil, fmt.Errorf("the assignment would cause the following constraint violation: %v", e) + if errors := constraint.Allowed(boundPodList.Items); len(errors) > 0 { + return nil, fmt.Errorf("the assignment would cause the following constraints violation: %v", errors) } return boundPodList, nil }) From fe27529d7e6bbefd7145c5da00d3782717eac262 Mon Sep 17 00:00:00 2001 From: nikhiljindal Date: Wed, 4 Feb 2015 20:19:00 -0800 Subject: [PATCH 47/50] Adding static swagger spec and a script to update it --- api/swagger-spec/api.json | 27 + api/swagger-spec/resourceListing.json | 30 + api/swagger-spec/v1beta1.json | 4247 +++++++++++++++++++++++++ api/swagger-spec/v1beta2.json | 4226 ++++++++++++++++++++++++ api/swagger-spec/v1beta3.json | 3812 ++++++++++++++++++++++ api/swagger-spec/version.json | 27 + hack/update-swagger-spec.sh | 71 + 7 files changed, 12440 insertions(+) create mode 100644 api/swagger-spec/api.json create mode 100644 api/swagger-spec/resourceListing.json create mode 100644 api/swagger-spec/v1beta1.json create mode 100644 api/swagger-spec/v1beta2.json create mode 100644 api/swagger-spec/v1beta3.json create mode 100644 api/swagger-spec/version.json create mode 100755 hack/update-swagger-spec.sh diff --git a/api/swagger-spec/api.json b/api/swagger-spec/api.json new file mode 100644 index 00000000000..251994b49cf --- /dev/null +++ b/api/swagger-spec/api.json @@ -0,0 +1,27 @@ +{ + "swaggerVersion": "1.2", + "apiVersion": "", + "basePath": "127.0.0.1:8050", + "resourcePath": "/api", + "apis": [ + { + "path": "/api", + "description": "get available api versions", + "operations": [ + { + "type": "void", + "method": "GET", + "summary": "get available api versions", + "nickname": "getApiVersions", + "parameters": [], + "produces": [ + "application/json" + ], + "consumes": [ + "application/json" + ] + } + ] + } + ] + } \ No newline at end of file diff --git a/api/swagger-spec/resourceListing.json b/api/swagger-spec/resourceListing.json new file mode 100644 index 00000000000..22cee15c893 --- /dev/null +++ b/api/swagger-spec/resourceListing.json @@ -0,0 +1,30 @@ +{ + "swaggerVersion": "1.2", + "apis": [ + { + "path": "/api/v1beta1", + "description": "API at /api/v1beta1 version v1beta1" + }, + { + "path": "/api/v1beta2", + "description": "API at /api/v1beta2 version v1beta2" + }, + { + "path": "/api/v1beta3", + "description": "API at /api/v1beta3 version v1beta3" + }, + { + "path": "/api", + "description": "get available api versions" + }, + { + "path": "/version", + "description": "git code version from which this is built" + } + ], + "apiVersion": "", + "info": { + "title": "", + "description": "" + } + } \ No newline at end of file diff --git a/api/swagger-spec/v1beta1.json b/api/swagger-spec/v1beta1.json new file mode 100644 index 00000000000..d22713500b3 --- /dev/null +++ b/api/swagger-spec/v1beta1.json @@ -0,0 +1,4247 @@ +{ + "swaggerVersion": "1.2", + "apiVersion": "v1beta1", + "basePath": "127.0.0.1:8050", + "resourcePath": "/api/v1beta1", + "apis": [ + { + "path": "/api/v1beta1/services", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.Service", + "method": "GET", + "summary": "list objects of kind Service", + "nickname": "listService", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "create a Service", + "nickname": "createService", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta1.Service", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/replicationControllers", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.ReplicationControllerList", + "method": "GET", + "summary": "list objects of kind ReplicationController", + "nickname": "listReplicationController", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "create a ReplicationController", + "nickname": "createReplicationController", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta1.ReplicationController", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/events", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.EventList", + "method": "GET", + "summary": "list objects of kind Event", + "nickname": "listEvent", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "create a Event", + "nickname": "createEvent", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta1.Event", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/watch/nodes/{name}", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.Minion", + "method": "GET", + "summary": "watch a particular Node", + "nickname": "watchNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/redirect/nodes/{name}", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "void", + "method": "GET", + "summary": "redirect GET request to Node", + "nickname": "redirectNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/proxy/nodes/{name}/{path:*}", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "void", + "method": "GET", + "summary": "proxy GET requests to Node", + "nickname": "proxyGETNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "proxy PUT requests to Node", + "nickname": "proxyPUTNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "proxy POST requests to Node", + "nickname": "proxyPOSTNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "proxy DELETE requests to Node", + "nickname": "proxyDELETENode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/watch/limitRanges", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.LimitRangeList", + "method": "GET", + "summary": "watch a list of LimitRange", + "nickname": "watchLimitRangelist", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/pods", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.PodList", + "method": "GET", + "summary": "list objects of kind Pod", + "nickname": "listPod", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "create a Pod", + "nickname": "createPod", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta1.Pod", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/endpoints", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.EndpointsList", + "method": "GET", + "summary": "list objects of kind Endpoints", + "nickname": "listEndpoints", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "create a Endpoints", + "nickname": "createEndpoints", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta1.Endpoints", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/watch/endpoints", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.EndpointsList", + "method": "GET", + "summary": "watch a list of Endpoints", + "nickname": "watchEndpointslist", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/redirect/minions/{name}", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "void", + "method": "GET", + "summary": "redirect GET request to Node", + "nickname": "redirectNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/proxy/minions/{name}/{path:*}", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "void", + "method": "GET", + "summary": "proxy GET requests to Node", + "nickname": "proxyGETNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "proxy PUT requests to Node", + "nickname": "proxyPUTNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "proxy POST requests to Node", + "nickname": "proxyPOSTNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "proxy DELETE requests to Node", + "nickname": "proxyDELETENode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/watch/pods", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.PodList", + "method": "GET", + "summary": "watch a list of Pod", + "nickname": "watchPodlist", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/watch/minions/{name}", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.Minion", + "method": "GET", + "summary": "watch a particular Node", + "nickname": "watchNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/watch/events/{name}", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.Event", + "method": "GET", + "summary": "watch a particular Event", + "nickname": "watchEvent", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Event", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/pods/{name}", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.Pod", + "method": "GET", + "summary": "read the specified Pod", + "nickname": "readPod", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Pod", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "update the specified Pod", + "nickname": "updatePod", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Pod", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta1.Pod", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "delete a Pod", + "nickname": "deletePod", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Pod", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/watch/replicationControllers/{name}", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.ReplicationController", + "method": "GET", + "summary": "watch a particular ReplicationController", + "nickname": "watchReplicationController", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the ReplicationController", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/endpoints/{name}", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.Endpoints", + "method": "GET", + "summary": "read the specified Endpoints", + "nickname": "readEndpoints", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Endpoints", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "update the specified Endpoints", + "nickname": "updateEndpoints", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Endpoints", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta1.Endpoints", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/resourceQuotas", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.ResourceQuotaList", + "method": "GET", + "summary": "list objects of kind ResourceQuota", + "nickname": "listResourceQuota", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "create a ResourceQuota", + "nickname": "createResourceQuota", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta1.ResourceQuota", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/proxy/pods/{name}/{path:*}", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "void", + "method": "GET", + "summary": "proxy GET requests to Pod", + "nickname": "proxyGETPod", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Pod", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "proxy PUT requests to Pod", + "nickname": "proxyPUTPod", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Pod", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "proxy POST requests to Pod", + "nickname": "proxyPOSTPod", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Pod", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "proxy DELETE requests to Pod", + "nickname": "proxyDELETEPod", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Pod", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/nodes", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.MinionList", + "method": "GET", + "summary": "list objects of kind Node", + "nickname": "listNode", + "parameters": [], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "create a Node", + "nickname": "createNode", + "parameters": [ + { + "type": "v1beta1.Minion", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/watch/limitRanges/{name}", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.LimitRange", + "method": "GET", + "summary": "watch a particular LimitRange", + "nickname": "watchLimitRange", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the LimitRange", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/proxy/services/{name}/{path:*}", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "void", + "method": "GET", + "summary": "proxy GET requests to Service", + "nickname": "proxyGETService", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Service", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "proxy PUT requests to Service", + "nickname": "proxyPUTService", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Service", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "proxy POST requests to Service", + "nickname": "proxyPOSTService", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Service", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "proxy DELETE requests to Service", + "nickname": "proxyDELETEService", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Service", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/redirect/pods/{name}", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "void", + "method": "GET", + "summary": "redirect GET request to Pod", + "nickname": "redirectPod", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Pod", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/bindings", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "void", + "method": "POST", + "summary": "create a Binding", + "nickname": "createBinding", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta1.Binding", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/events/{name}", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.Event", + "method": "GET", + "summary": "read the specified Event", + "nickname": "readEvent", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Event", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "delete a Event", + "nickname": "deleteEvent", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Event", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/watch/services", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.Service", + "method": "GET", + "summary": "watch a list of Service", + "nickname": "watchServicelist", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/watch/services/{name}", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.Service", + "method": "GET", + "summary": "watch a particular Service", + "nickname": "watchService", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Service", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/nodes/{name}", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.Minion", + "method": "GET", + "summary": "read the specified Node", + "nickname": "readNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "update the specified Node", + "nickname": "updateNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + }, + { + "type": "v1beta1.Minion", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "delete a Node", + "nickname": "deleteNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/limitRanges", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.LimitRangeList", + "method": "GET", + "summary": "list objects of kind LimitRange", + "nickname": "listLimitRange", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "create a LimitRange", + "nickname": "createLimitRange", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta1.LimitRange", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/watch/replicationControllers", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.ReplicationControllerList", + "method": "GET", + "summary": "watch a list of ReplicationController", + "nickname": "watchReplicationControllerlist", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/minions", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.MinionList", + "method": "GET", + "summary": "list objects of kind Node", + "nickname": "listNode", + "parameters": [], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "create a Node", + "nickname": "createNode", + "parameters": [ + { + "type": "v1beta1.Minion", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/watch/minions", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.MinionList", + "method": "GET", + "summary": "watch a list of Node", + "nickname": "watchNodelist", + "parameters": [], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/resourceQuotas/{name}", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.ResourceQuota", + "method": "GET", + "summary": "read the specified ResourceQuota", + "nickname": "readResourceQuota", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the ResourceQuota", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "update the specified ResourceQuota", + "nickname": "updateResourceQuota", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the ResourceQuota", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta1.ResourceQuota", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "delete a ResourceQuota", + "nickname": "deleteResourceQuota", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the ResourceQuota", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/services/{name}", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.Service", + "method": "GET", + "summary": "read the specified Service", + "nickname": "readService", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Service", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "update the specified Service", + "nickname": "updateService", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Service", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta1.Service", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "delete a Service", + "nickname": "deleteService", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Service", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/minions/{name}", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.Minion", + "method": "GET", + "summary": "read the specified Node", + "nickname": "readNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "update the specified Node", + "nickname": "updateNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + }, + { + "type": "v1beta1.Minion", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "delete a Node", + "nickname": "deleteNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/watch/events", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.EventList", + "method": "GET", + "summary": "watch a list of Event", + "nickname": "watchEventlist", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/redirect/services/{name}", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "void", + "method": "GET", + "summary": "redirect GET request to Service", + "nickname": "redirectService", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Service", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/limitRanges/{name}", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.LimitRange", + "method": "GET", + "summary": "read the specified LimitRange", + "nickname": "readLimitRange", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the LimitRange", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "update the specified LimitRange", + "nickname": "updateLimitRange", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the LimitRange", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta1.LimitRange", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "delete a LimitRange", + "nickname": "deleteLimitRange", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the LimitRange", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/watch/pods/{name}", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.Pod", + "method": "GET", + "summary": "watch a particular Pod", + "nickname": "watchPod", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Pod", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/replicationControllers/{name}", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.ReplicationController", + "method": "GET", + "summary": "read the specified ReplicationController", + "nickname": "readReplicationController", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the ReplicationController", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "update the specified ReplicationController", + "nickname": "updateReplicationController", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the ReplicationController", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta1.ReplicationController", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "delete a ReplicationController", + "nickname": "deleteReplicationController", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the ReplicationController", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/watch/resourceQuotas", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.ResourceQuotaList", + "method": "GET", + "summary": "watch a list of ResourceQuota", + "nickname": "watchResourceQuotalist", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/watch/resourceQuotas/{name}", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.ResourceQuota", + "method": "GET", + "summary": "watch a particular ResourceQuota", + "nickname": "watchResourceQuota", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the ResourceQuota", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/resourceQuotaUsages", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "void", + "method": "POST", + "summary": "create a ResourceQuotaUsage", + "nickname": "createResourceQuotaUsage", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta1.ResourceQuotaUsage", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/watch/endpoints/{name}", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.Endpoints", + "method": "GET", + "summary": "watch a particular Endpoints", + "nickname": "watchEndpoints", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Endpoints", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta1/watch/nodes", + "description": "API at /api/v1beta1 version v1beta1", + "operations": [ + { + "type": "v1beta1.MinionList", + "method": "GET", + "summary": "watch a list of Node", + "nickname": "watchNodelist", + "parameters": [], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + } + ], + "models": { + "v1beta1.Binding": { + "id": "v1beta1.Binding", + "required": [ + "podID", + "host" + ], + "properties": { + "annotations": { + "type": "v1beta1.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "host": { + "type": "string", + "description": "host to which to bind the specified pod" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "podID": { + "type": "string", + "description": "name of the pod to bind" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta1.Capabilities": { + "id": "v1beta1.Capabilities", + "properties": { + "add": { + "type": "array", + "items": [ + { + "$ref": "v1beta1.CapabilityType" + } + ], + "description": "added capabilities" + }, + "drop": { + "type": "array", + "items": [ + { + "$ref": "v1beta1.CapabilityType" + } + ], + "description": "droped capabilities" + } + } + }, + "v1beta1.CapabilityType": { + "id": "v1beta1.CapabilityType", + "properties": {} + }, + "v1beta1.Container": { + "id": "v1beta1.Container", + "required": [ + "name", + "image", + "imagePullPolicy" + ], + "properties": { + "capabilities": { + "type": "v1beta1.Capabilities", + "description": "capabilities for container" + }, + "command": { + "type": "array", + "items": [ + { + "$ref": "string" + } + ], + "description": "command argv array; not executed within a shell; defaults to entrypoint or command in the image" + }, + "cpu": { + "type": "integer", + "format": "int32", + "description": "CPU share in thousandths of a core" + }, + "env": { + "type": "array", + "items": [ + { + "$ref": "v1beta1.EnvVar" + } + ], + "description": "list of environment variables to set in the container" + }, + "image": { + "type": "string", + "description": "Docker image name" + }, + "imagePullPolicy": { + "type": "v1beta1.PullPolicy", + "description": "image pull policy; one of PullAlways, PullNever, PullIfNotPresent; defaults to PullAlways if :latest tag is specified, or PullIfNotPresent otherwise" + }, + "lifecycle": { + "type": "v1beta1.Lifecycle", + "description": "actions that the management system should take in response to container lifecycle events" + }, + "livenessProbe": { + "type": "v1beta1.LivenessProbe", + "description": "periodic probe of container liveness; container will be restarted if the probe fails" + }, + "memory": { + "type": "integer", + "format": "int64", + "description": "memory limit in bytes; defaults to unlimited" + }, + "name": { + "type": "string", + "description": "name of the container; must be a DNS_LABEL and unique within the pod" + }, + "ports": { + "type": "array", + "items": [ + { + "$ref": "v1beta1.Port" + } + ], + "description": "list of ports to expose from the container" + }, + "privileged": { + "type": "boolean", + "description": "whether or not the container is granted privileged status; defaults to false" + }, + "resources": { + "type": "v1beta1.ResourceRequirementSpec", + "description": "Compute Resources required by this container" + }, + "terminationMessagePath": { + "type": "string", + "description": "path at which the file to which the container's termination message will be written is mounted into the container's filesystem; message written is intended to be brief final status, such as an assertion failure message; defaults to /dev/termination-log" + }, + "volumeMounts": { + "type": "array", + "items": [ + { + "$ref": "v1beta1.VolumeMount" + } + ], + "description": "pod volumes to mount into the container's filesystem" + }, + "workingDir": { + "type": "string", + "description": "container's working directory; defaults to image's default" + } + } + }, + "v1beta1.ContainerManifest": { + "id": "v1beta1.ContainerManifest", + "required": [ + "version", + "id", + "volumes", + "containers" + ], + "properties": { + "containers": { + "type": "array", + "items": [ + { + "$ref": "v1beta1.Container" + } + ], + "description": "list of containers belonging to the pod" + }, + "dnsPolicy": { + "type": "v1beta1.DNSPolicy", + "description": "DNS policy for containers within the pod; one of 'ClusterFirst' or 'Default'" + }, + "id": { + "type": "string", + "description": "manifest name; must be a DNS_SUBDOMAIN" + }, + "restartPolicy": { + "type": "v1beta1.RestartPolicy", + "description": "restart policy for all containers within the pod; one of RestartPolicyAlways, RestartPolicyOnFailure, RestartPolicyNever" + }, + "uuid": { + "type": "types.UID", + "description": "manifest UUID" + }, + "version": { + "type": "string", + "description": "manifest version; must be v1beta1" + }, + "volumes": { + "type": "array", + "items": [ + { + "$ref": "v1beta1.Volume" + } + ], + "description": "list of volumes that can be mounted by containers belonging to the pod" + } + } + }, + "v1beta1.EmptyDir": { + "id": "v1beta1.EmptyDir", + "properties": {} + }, + "v1beta1.Endpoints": { + "id": "v1beta1.Endpoints", + "properties": { + "annotations": { + "type": "v1beta1.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "endpoints": { + "type": "array", + "items": [ + { + "$ref": "string" + } + ], + "description": "list of endpoints corresponding to a service, of the form address:port, such as 10.10.1.1:1909" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta1.EndpointsList": { + "id": "v1beta1.EndpointsList", + "required": [ + "items" + ], + "properties": { + "annotations": { + "type": "v1beta1.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "items": { + "type": "array", + "items": [ + { + "$ref": "v1beta1.Endpoints" + } + ], + "description": "list of service endpoint lists" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta1.EnvVar": { + "id": "v1beta1.EnvVar", + "required": [ + "name" + ], + "properties": { + "key": { + "type": "string", + "description": "name of the environment variable; must be a C_IDENTIFIER; deprecated - use name instead" + }, + "name": { + "type": "string", + "description": "name of the environment variable; must be a C_IDENTIFIER" + }, + "value": { + "type": "string", + "description": "value of the environment variable; defaults to empty string" + } + } + }, + "v1beta1.Event": { + "id": "v1beta1.Event", + "properties": { + "annotations": { + "type": "v1beta1.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "host": { + "type": "string", + "description": "host name on which this event was generated" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "involvedObject": { + "type": "v1beta1.ObjectReference", + "description": "object that this event is about" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "message": { + "type": "string", + "description": "human-readable description of the status of this operation" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "reason": { + "type": "string", + "description": "short, machine understandable string that gives the reason for the transition into the object's current status" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "source": { + "type": "string", + "description": "component reporting this event; short machine understandable string" + }, + "status": { + "type": "string", + "description": "short, machine understandable string that describes the current status of the referred object" + }, + "timestamp": { + "type": "string", + "description": "time at which the client recorded the event" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta1.EventList": { + "id": "v1beta1.EventList", + "required": [ + "items" + ], + "properties": { + "annotations": { + "type": "v1beta1.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "items": { + "type": "array", + "items": [ + { + "$ref": "v1beta1.Event" + } + ], + "description": "list of events" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta1.ExecAction": { + "id": "v1beta1.ExecAction", + "properties": { + "command": { + "type": "array", + "items": [ + { + "$ref": "string" + } + ], + "description": "command line to execute inside the container; working directory for the command is root ('/') in the container's file system; the command is exec'd, not run inside a shell; exit status of 0 is treated as live/healthy and non-zero is unhealthy" + } + } + }, + "v1beta1.GCEPersistentDisk": { + "id": "v1beta1.GCEPersistentDisk", + "required": [ + "pdName" + ], + "properties": { + "fsType": { + "type": "string", + "description": "file system type to mount, such as ext4, xfs, ntfs" + }, + "partition": { + "type": "integer", + "format": "int32", + "description": "partition on the disk to mount (e.g., '1' for /dev/sda1); if omitted the plain device name (e.g., /dev/sda) will be mounted" + }, + "pdName": { + "type": "string", + "description": "unique name of the PD resource in GCE" + }, + "readOnly": { + "type": "boolean", + "description": "read-only if true, read-write otherwise (false or unspecified)" + } + } + }, + "v1beta1.GitRepo": { + "id": "v1beta1.GitRepo", + "required": [ + "repository", + "revision" + ], + "properties": { + "repository": { + "type": "string", + "description": "repository URL" + }, + "revision": { + "type": "string", + "description": "commit hash for the specified revision" + } + } + }, + "v1beta1.HTTPGetAction": { + "id": "v1beta1.HTTPGetAction", + "properties": { + "host": { + "type": "string", + "description": "hostname to connect to; defaults to pod IP" + }, + "path": { + "type": "string", + "description": "path to access on the HTTP server" + }, + "port": { + "type": "string", + "description": "number or name of the port to access on the container" + } + } + }, + "v1beta1.Handler": { + "id": "v1beta1.Handler", + "properties": { + "exec": { + "type": "v1beta1.ExecAction", + "description": "exec-based handler" + }, + "httpGet": { + "type": "v1beta1.HTTPGetAction", + "description": "HTTP-based handler" + }, + "tcpSocket": { + "type": "v1beta1.TCPSocketAction", + "description": "TCP-based handler; TCP hooks not yet supported" + } + } + }, + "v1beta1.HostPath": { + "id": "v1beta1.HostPath", + "required": [ + "path" + ], + "properties": { + "path": { + "type": "string", + "description": "path of the directory on the host" + } + } + }, + "v1beta1.Lifecycle": { + "id": "v1beta1.Lifecycle", + "properties": { + "postStart": { + "type": "v1beta1.Handler", + "description": "called immediately after a container is started; if the handler fails, the container is terminated and restarted according to its restart policy; other management of the container blocks until the hook completes" + }, + "preStop": { + "type": "v1beta1.Handler", + "description": "called before a container is terminated; the container is terminated after the handler completes; other management of the container blocks until the hook completes" + } + } + }, + "v1beta1.LimitRange": { + "id": "v1beta1.LimitRange", + "properties": { + "annotations": { + "type": "v1beta1.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "spec": { + "type": "v1beta1.LimitRangeSpec" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta1.LimitRangeItem": { + "id": "v1beta1.LimitRangeItem", + "properties": { + "max": { + "type": "v1beta1.ResourceList" + }, + "min": { + "type": "v1beta1.ResourceList" + }, + "type": { + "type": "v1beta1.LimitType" + } + } + }, + "v1beta1.LimitRangeList": { + "id": "v1beta1.LimitRangeList", + "required": [ + "items" + ], + "properties": { + "annotations": { + "type": "v1beta1.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "items": { + "type": "array", + "items": [ + { + "$ref": "v1beta1.LimitRange" + } + ] + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta1.LimitRangeSpec": { + "id": "v1beta1.LimitRangeSpec", + "required": [ + "limits" + ], + "properties": { + "limits": { + "type": "array", + "items": [ + { + "$ref": "v1beta1.LimitRangeItem" + } + ] + } + } + }, + "v1beta1.LivenessProbe": { + "id": "v1beta1.LivenessProbe", + "properties": { + "exec": { + "type": "v1beta1.ExecAction", + "description": "parameters for exec-based liveness probe" + }, + "httpGet": { + "type": "v1beta1.HTTPGetAction", + "description": "parameters for HTTP-based liveness probe" + }, + "initialDelaySeconds": { + "type": "integer", + "format": "int64", + "description": "number of seconds after the container has started before liveness probes are initiated" + }, + "tcpSocket": { + "type": "v1beta1.TCPSocketAction", + "description": "parameters for TCP-based liveness probe" + } + } + }, + "v1beta1.Minion": { + "id": "v1beta1.Minion", + "properties": { + "annotations": { + "type": "v1beta1.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "hostIP": { + "type": "string", + "description": "IP address of the node" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "labels": { + "type": "v1beta1.Minion.labels", + "description": "map of string keys and values that can be used to organize and categorize minions; labels of a minion assigned by the scheduler must match the scheduled pod's nodeSelector" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "resources": { + "type": "v1beta1.NodeResources", + "description": "characterization of node resources" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "status": { + "type": "v1beta1.NodeStatus", + "description": "current status of node" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta1.Minion.labels": { + "id": "v1beta1.Minion.labels", + "properties": {} + }, + "v1beta1.MinionList": { + "id": "v1beta1.MinionList", + "required": [ + "items" + ], + "properties": { + "annotations": { + "type": "v1beta1.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "items": { + "type": "array", + "items": [ + { + "$ref": "v1beta1.Minion" + } + ], + "description": "list of nodes" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "minions": { + "type": "array", + "items": [ + { + "$ref": "v1beta1.Minion" + } + ], + "description": "list of nodes; deprecated" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta1.NodeCondition": { + "id": "v1beta1.NodeCondition", + "required": [ + "kind", + "status" + ], + "properties": { + "kind": { + "type": "v1beta1.NodeConditionKind", + "description": "kind of the condition, one of reachable, ready" + }, + "lastTransitionTime": { + "type": "string", + "description": "last time the condition transit from one status to another" + }, + "message": { + "type": "string", + "description": "human readable message indicating details about last transition" + }, + "reason": { + "type": "string", + "description": "(brief) reason for the condition's last transition" + }, + "status": { + "type": "v1beta1.NodeConditionStatus", + "description": "status of the condition, one of full, none, unknown" + } + } + }, + "v1beta1.NodeResources": { + "id": "v1beta1.NodeResources", + "properties": { + "capacity": { + "type": "v1beta1.ResourceList", + "description": "resource capacity of a node represented as a map of resource name to quantity of resource" + } + } + }, + "v1beta1.NodeStatus": { + "id": "v1beta1.NodeStatus", + "properties": { + "conditions": { + "type": "array", + "items": [ + { + "$ref": "v1beta1.NodeCondition" + } + ], + "description": "conditions is an array of current node conditions" + }, + "phase": { + "type": "v1beta1.NodePhase", + "description": "node phase is the current lifecycle phase of the node" + } + } + }, + "v1beta1.ObjectReference": { + "id": "v1beta1.ObjectReference", + "properties": { + "apiVersion": { + "type": "string", + "description": "API version of the referent" + }, + "fieldPath": { + "type": "string", + "description": "if referring to a piece of an object instead of an entire object, this string should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]" + }, + "kind": { + "type": "string", + "description": "kind of the referent" + }, + "name": { + "type": "string", + "description": "id of the referent" + }, + "namespace": { + "type": "string", + "description": "namespace of the referent" + }, + "resourceVersion": { + "type": "string", + "description": "specific resourceVersion to which this reference is made, if any" + }, + "uid": { + "type": "types.UID", + "description": "uid of the referent" + } + } + }, + "v1beta1.Pod": { + "id": "v1beta1.Pod", + "properties": { + "annotations": { + "type": "v1beta1.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "currentState": { + "type": "v1beta1.PodState", + "description": "current state of the pod" + }, + "desiredState": { + "type": "v1beta1.PodState", + "description": "specification of the desired state of the pod" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "labels": { + "type": "v1beta1.Pod.labels", + "description": "map of string keys and values that can be used to organize and categorize pods; may match selectors of replication controllers and services" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "nodeSelector": { + "type": "v1beta1.Pod.nodeSelector", + "description": "selector which must match a node's labels for the pod to be scheduled on that node" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta1.Pod.labels": { + "id": "v1beta1.Pod.labels", + "properties": {} + }, + "v1beta1.Pod.nodeSelector": { + "id": "v1beta1.Pod.nodeSelector", + "properties": {} + }, + "v1beta1.PodList": { + "id": "v1beta1.PodList", + "required": [ + "items" + ], + "properties": { + "annotations": { + "type": "v1beta1.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "items": { + "type": "array", + "items": [ + { + "$ref": "v1beta1.Pod" + } + ], + "description": "list of pods" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta1.PodState": { + "id": "v1beta1.PodState", + "properties": { + "host": { + "type": "string", + "description": "host to which the pod is assigned; empty if not yet scheduled" + }, + "hostIP": { + "type": "string", + "description": "IP address of the host to which the pod is assigned; empty if not yet scheduled" + }, + "info": { + "type": "v1beta1.PodInfo", + "description": "map of container name to container status" + }, + "manifest": { + "type": "v1beta1.ContainerManifest", + "description": "manifest of containers and volumes comprising the pod" + }, + "message": { + "type": "string", + "description": "human readable message indicating details about why the pod is in this condition" + }, + "podIP": { + "type": "string", + "description": "IP address allocated to the pod; routable at least within the cluster; empty if not yet allocated" + }, + "status": { + "type": "v1beta1.PodStatus", + "description": "current condition of the pod, Waiting, Running, or Terminated" + } + } + }, + "v1beta1.PodTemplate": { + "id": "v1beta1.PodTemplate", + "properties": { + "annotations": { + "type": "v1beta1.PodTemplate.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about pods created from the template" + }, + "desiredState": { + "type": "v1beta1.PodState", + "description": "specification of the desired state of pods created from this template" + }, + "labels": { + "type": "v1beta1.PodTemplate.labels", + "description": "map of string keys and values that can be used to organize and categorize the pods created from the template; must match the selector of the replication controller to which the template belongs; may match selectors of services" + }, + "nodeSelector": { + "type": "v1beta1.PodTemplate.nodeSelector", + "description": "a selector which must be true for the pod to fit on a node" + } + } + }, + "v1beta1.PodTemplate.annotations": { + "id": "v1beta1.PodTemplate.annotations", + "properties": {} + }, + "v1beta1.PodTemplate.labels": { + "id": "v1beta1.PodTemplate.labels", + "properties": {} + }, + "v1beta1.PodTemplate.nodeSelector": { + "id": "v1beta1.PodTemplate.nodeSelector", + "properties": {} + }, + "v1beta1.Port": { + "id": "v1beta1.Port", + "required": [ + "containerPort" + ], + "properties": { + "containerPort": { + "type": "integer", + "format": "int32", + "description": "number of port to expose on the pod's IP address" + }, + "hostIP": { + "type": "string", + "description": "host IP to bind the port to" + }, + "hostPort": { + "type": "integer", + "format": "int32", + "description": "number of port to expose on the host; most containers do not need this" + }, + "name": { + "type": "string", + "description": "name for the port that can be referred to by services; must be a DNS_LABEL and unique without the pod" + }, + "protocol": { + "type": "v1beta1.Protocol", + "description": "protocol for port; must be UDP or TCP; TCP if unspecified" + } + } + }, + "v1beta1.ReplicationController": { + "id": "v1beta1.ReplicationController", + "properties": { + "annotations": { + "type": "v1beta1.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "currentState": { + "type": "v1beta1.ReplicationControllerState", + "description": "current state of the replication controller" + }, + "desiredState": { + "type": "v1beta1.ReplicationControllerState", + "description": "specification of the desired state of the replication controller" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "labels": { + "type": "v1beta1.ReplicationController.labels", + "description": "map of string keys and values that can be used to organize and categorize replication controllers" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta1.ReplicationController.labels": { + "id": "v1beta1.ReplicationController.labels", + "properties": {} + }, + "v1beta1.ReplicationControllerList": { + "id": "v1beta1.ReplicationControllerList", + "required": [ + "items" + ], + "properties": { + "annotations": { + "type": "v1beta1.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "items": { + "type": "array", + "items": [ + { + "$ref": "v1beta1.ReplicationController" + } + ], + "description": "list of replication controllers" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta1.ReplicationControllerState": { + "id": "v1beta1.ReplicationControllerState", + "required": [ + "replicas" + ], + "properties": { + "podTemplate": { + "type": "v1beta1.PodTemplate", + "description": "template for pods to be created by this replication controller when the observed number of replicas is less than the desired number of replicas" + }, + "replicaSelector": { + "type": "v1beta1.ReplicationControllerState.replicaSelector", + "description": "label keys and values that must match in order to be controlled by this replication controller" + }, + "replicas": { + "type": "integer", + "format": "int32", + "description": "number of replicas (desired or observed, as appropriate)" + } + } + }, + "v1beta1.ReplicationControllerState.replicaSelector": { + "id": "v1beta1.ReplicationControllerState.replicaSelector", + "properties": {} + }, + "v1beta1.ResourceQuota": { + "id": "v1beta1.ResourceQuota", + "properties": { + "annotations": { + "type": "v1beta1.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "spec": { + "type": "v1beta1.ResourceQuotaSpec" + }, + "status": { + "type": "v1beta1.ResourceQuotaStatus" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta1.ResourceQuotaList": { + "id": "v1beta1.ResourceQuotaList", + "required": [ + "items" + ], + "properties": { + "annotations": { + "type": "v1beta1.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "items": { + "type": "array", + "items": [ + { + "$ref": "v1beta1.ResourceQuota" + } + ] + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta1.ResourceQuotaSpec": { + "id": "v1beta1.ResourceQuotaSpec", + "properties": { + "hard": { + "type": "v1beta1.ResourceList" + } + } + }, + "v1beta1.ResourceQuotaStatus": { + "id": "v1beta1.ResourceQuotaStatus", + "properties": { + "hard": { + "type": "v1beta1.ResourceList" + }, + "used": { + "type": "v1beta1.ResourceList" + } + } + }, + "v1beta1.ResourceQuotaUsage": { + "id": "v1beta1.ResourceQuotaUsage", + "properties": { + "annotations": { + "type": "v1beta1.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "status": { + "type": "v1beta1.ResourceQuotaStatus" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta1.ResourceRequirementSpec": { + "id": "v1beta1.ResourceRequirementSpec", + "properties": { + "limits": { + "type": "v1beta1.ResourceList", + "description": "Maximum amount of compute resources allowed" + } + } + }, + "v1beta1.RestartPolicy": { + "id": "v1beta1.RestartPolicy", + "properties": { + "always": { + "type": "v1beta1.RestartPolicyAlways", + "description": "always restart the container after termination" + }, + "never": { + "type": "v1beta1.RestartPolicyNever", + "description": "never restart the container" + }, + "onFailure": { + "type": "v1beta1.RestartPolicyOnFailure", + "description": "restart the container if it fails for any reason, but not if it succeeds (exit 0)" + } + } + }, + "v1beta1.RestartPolicyAlways": { + "id": "v1beta1.RestartPolicyAlways", + "properties": {} + }, + "v1beta1.RestartPolicyNever": { + "id": "v1beta1.RestartPolicyNever", + "properties": {} + }, + "v1beta1.RestartPolicyOnFailure": { + "id": "v1beta1.RestartPolicyOnFailure", + "properties": {} + }, + "v1beta1.Service": { + "id": "v1beta1.Service", + "required": [ + "port", + "selector" + ], + "properties": { + "annotations": { + "type": "v1beta1.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "containerPort": { + "type": "string", + "description": "number or name of the port to access on the containers belonging to pods targeted by the service" + }, + "createExternalLoadBalancer": { + "type": "boolean", + "description": "set up a cloud-provider-specific load balancer on an external IP" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "labels": { + "type": "v1beta1.Service.labels", + "description": "map of string keys and values that can be used to organize and categorize services" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "port": { + "type": "integer", + "format": "int32", + "description": "port exposed by the service" + }, + "portalIP": { + "type": "string", + "description": "IP address of the service; usually assigned by the system; if specified, it will be allocated to the service if unused, and creation of the service will fail otherwise; cannot be updated" + }, + "protocol": { + "type": "v1beta1.Protocol", + "description": "protocol for port; must be UDP or TCP; TCP if unspecified" + }, + "proxyPort": { + "type": "integer", + "format": "int32", + "description": "if non-zero, a pre-allocated host port used for this service by the proxy on each node; assigned by the master and ignored on input" + }, + "publicIPs": { + "type": "array", + "items": [ + { + "$ref": "string" + } + ], + "description": "externally visible IPs from which to select the address for the external load balancer" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selector": { + "type": "v1beta1.Service.selector", + "description": "label keys and values that must match in order to receive traffic for this service; if empty, all pods are selected, if not specified, endpoints must be manually specified" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "sessionAffinity": { + "type": "v1beta1.AffinityType", + "description": "enable client IP based session affinity; must be ClientIP or None; defaults to None" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta1.Service.labels": { + "id": "v1beta1.Service.labels", + "properties": {} + }, + "v1beta1.Service.selector": { + "id": "v1beta1.Service.selector", + "properties": {} + }, + "v1beta1.TCPSocketAction": { + "id": "v1beta1.TCPSocketAction", + "properties": { + "port": { + "type": "string", + "description": "number of name of the port to access on the container" + } + } + }, + "v1beta1.Volume": { + "id": "v1beta1.Volume", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "description": "volume name; must be a DNS_LABEL and unique within the pod" + }, + "source": { + "type": "v1beta1.VolumeSource", + "description": "location and type of volume to mount; at most one of HostDir, EmptyDir, GCEPersistentDisk, or GitRepo; default is EmptyDir" + } + } + }, + "v1beta1.VolumeMount": { + "id": "v1beta1.VolumeMount", + "required": [ + "name", + "mountPath" + ], + "properties": { + "mountPath": { + "type": "string", + "description": "path within the container at which the volume should be mounted; overrides path" + }, + "mountType": { + "type": "string", + "description": "LOCAL or HOST; defaults to LOCAL; deprecated" + }, + "name": { + "type": "string", + "description": "name of the volume to mount" + }, + "path": { + "type": "string", + "description": "path within the container at which the volume should be mounted; deprecated" + }, + "readOnly": { + "type": "boolean", + "description": "mounted read-only if true, read-write otherwise (false or unspecified)" + } + } + }, + "v1beta1.VolumeSource": { + "id": "v1beta1.VolumeSource", + "required": [ + "hostDir", + "emptyDir", + "persistentDisk", + "gitRepo" + ], + "properties": { + "emptyDir": { + "type": "v1beta1.EmptyDir", + "description": "temporary directory that shares a pod's lifetime" + }, + "gitRepo": { + "type": "v1beta1.GitRepo", + "description": "git repository at a particular revision" + }, + "hostDir": { + "type": "v1beta1.HostPath", + "description": "pre-existing host file or directory; generally for privileged system daemons or other agents tied to the host" + }, + "persistentDisk": { + "type": "v1beta1.GCEPersistentDisk", + "description": "GCE disk resource attached to the host machine on demand" + } + } + } + } + } \ No newline at end of file diff --git a/api/swagger-spec/v1beta2.json b/api/swagger-spec/v1beta2.json new file mode 100644 index 00000000000..d3664433f4b --- /dev/null +++ b/api/swagger-spec/v1beta2.json @@ -0,0 +1,4226 @@ +{ + "swaggerVersion": "1.2", + "apiVersion": "v1beta2", + "basePath": "127.0.0.1:8050", + "resourcePath": "/api/v1beta2", + "apis": [ + { + "path": "/api/v1beta2/watch/endpoints", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.EndpointsList", + "method": "GET", + "summary": "watch a list of Endpoints", + "nickname": "watchEndpointslist", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/pods/{name}", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.Pod", + "method": "GET", + "summary": "read the specified Pod", + "nickname": "readPod", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Pod", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "update the specified Pod", + "nickname": "updatePod", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Pod", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta2.Pod", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "delete a Pod", + "nickname": "deletePod", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Pod", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/redirect/pods/{name}", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "void", + "method": "GET", + "summary": "redirect GET request to Pod", + "nickname": "redirectPod", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Pod", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/minions/{name}", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.Minion", + "method": "GET", + "summary": "read the specified Node", + "nickname": "readNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "update the specified Node", + "nickname": "updateNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + }, + { + "type": "v1beta2.Minion", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "delete a Node", + "nickname": "deleteNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/watch/services/{name}", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.Service", + "method": "GET", + "summary": "watch a particular Service", + "nickname": "watchService", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Service", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/limitRanges", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.LimitRangeList", + "method": "GET", + "summary": "list objects of kind LimitRange", + "nickname": "listLimitRange", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "create a LimitRange", + "nickname": "createLimitRange", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta2.LimitRange", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/watch/limitRanges", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.LimitRangeList", + "method": "GET", + "summary": "watch a list of LimitRange", + "nickname": "watchLimitRangelist", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/resourceQuotas", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.ResourceQuotaList", + "method": "GET", + "summary": "list objects of kind ResourceQuota", + "nickname": "listResourceQuota", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "create a ResourceQuota", + "nickname": "createResourceQuota", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta2.ResourceQuota", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/watch/resourceQuotas/{name}", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.ResourceQuota", + "method": "GET", + "summary": "watch a particular ResourceQuota", + "nickname": "watchResourceQuota", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the ResourceQuota", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/redirect/minions/{name}", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "void", + "method": "GET", + "summary": "redirect GET request to Node", + "nickname": "redirectNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/redirect/services/{name}", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "void", + "method": "GET", + "summary": "redirect GET request to Service", + "nickname": "redirectService", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Service", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/redirect/nodes/{name}", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "void", + "method": "GET", + "summary": "redirect GET request to Node", + "nickname": "redirectNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/proxy/nodes/{name}/{path:*}", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "void", + "method": "GET", + "summary": "proxy GET requests to Node", + "nickname": "proxyGETNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "proxy PUT requests to Node", + "nickname": "proxyPUTNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "proxy POST requests to Node", + "nickname": "proxyPOSTNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "proxy DELETE requests to Node", + "nickname": "proxyDELETENode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/replicationControllers", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.ReplicationControllerList", + "method": "GET", + "summary": "list objects of kind ReplicationController", + "nickname": "listReplicationController", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "create a ReplicationController", + "nickname": "createReplicationController", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta2.ReplicationController", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/limitRanges/{name}", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.LimitRange", + "method": "GET", + "summary": "read the specified LimitRange", + "nickname": "readLimitRange", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the LimitRange", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "update the specified LimitRange", + "nickname": "updateLimitRange", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the LimitRange", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta2.LimitRange", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "delete a LimitRange", + "nickname": "deleteLimitRange", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the LimitRange", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/bindings", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "void", + "method": "POST", + "summary": "create a Binding", + "nickname": "createBinding", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta2.Binding", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/watch/minions/{name}", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.Minion", + "method": "GET", + "summary": "watch a particular Node", + "nickname": "watchNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/watch/events", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.EventList", + "method": "GET", + "summary": "watch a list of Event", + "nickname": "watchEventlist", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/events/{name}", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.Event", + "method": "GET", + "summary": "read the specified Event", + "nickname": "readEvent", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Event", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "delete a Event", + "nickname": "deleteEvent", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Event", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/services", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.Service", + "method": "GET", + "summary": "list objects of kind Service", + "nickname": "listService", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "create a Service", + "nickname": "createService", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta2.Service", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/replicationControllers/{name}", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.ReplicationController", + "method": "GET", + "summary": "read the specified ReplicationController", + "nickname": "readReplicationController", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the ReplicationController", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "update the specified ReplicationController", + "nickname": "updateReplicationController", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the ReplicationController", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta2.ReplicationController", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "delete a ReplicationController", + "nickname": "deleteReplicationController", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the ReplicationController", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/resourceQuotas/{name}", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.ResourceQuota", + "method": "GET", + "summary": "read the specified ResourceQuota", + "nickname": "readResourceQuota", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the ResourceQuota", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "update the specified ResourceQuota", + "nickname": "updateResourceQuota", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the ResourceQuota", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta2.ResourceQuota", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "delete a ResourceQuota", + "nickname": "deleteResourceQuota", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the ResourceQuota", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/pods", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.PodList", + "method": "GET", + "summary": "list objects of kind Pod", + "nickname": "listPod", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "create a Pod", + "nickname": "createPod", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta2.Pod", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/proxy/minions/{name}/{path:*}", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "void", + "method": "GET", + "summary": "proxy GET requests to Node", + "nickname": "proxyGETNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "proxy PUT requests to Node", + "nickname": "proxyPUTNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "proxy POST requests to Node", + "nickname": "proxyPOSTNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "proxy DELETE requests to Node", + "nickname": "proxyDELETENode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/endpoints", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.EndpointsList", + "method": "GET", + "summary": "list objects of kind Endpoints", + "nickname": "listEndpoints", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "create a Endpoints", + "nickname": "createEndpoints", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta2.Endpoints", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/watch/nodes/{name}", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.Minion", + "method": "GET", + "summary": "watch a particular Node", + "nickname": "watchNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/watch/replicationControllers", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.ReplicationControllerList", + "method": "GET", + "summary": "watch a list of ReplicationController", + "nickname": "watchReplicationControllerlist", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/watch/resourceQuotas", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.ResourceQuotaList", + "method": "GET", + "summary": "watch a list of ResourceQuota", + "nickname": "watchResourceQuotalist", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/watch/limitRanges/{name}", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.LimitRange", + "method": "GET", + "summary": "watch a particular LimitRange", + "nickname": "watchLimitRange", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the LimitRange", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/minions", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.MinionList", + "method": "GET", + "summary": "list objects of kind Node", + "nickname": "listNode", + "parameters": [], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "create a Node", + "nickname": "createNode", + "parameters": [ + { + "type": "v1beta2.Minion", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/proxy/services/{name}/{path:*}", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "void", + "method": "GET", + "summary": "proxy GET requests to Service", + "nickname": "proxyGETService", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Service", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "proxy PUT requests to Service", + "nickname": "proxyPUTService", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Service", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "proxy POST requests to Service", + "nickname": "proxyPOSTService", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Service", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "proxy DELETE requests to Service", + "nickname": "proxyDELETEService", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Service", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/nodes", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.MinionList", + "method": "GET", + "summary": "list objects of kind Node", + "nickname": "listNode", + "parameters": [], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "create a Node", + "nickname": "createNode", + "parameters": [ + { + "type": "v1beta2.Minion", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/nodes/{name}", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.Minion", + "method": "GET", + "summary": "read the specified Node", + "nickname": "readNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "update the specified Node", + "nickname": "updateNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + }, + { + "type": "v1beta2.Minion", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "delete a Node", + "nickname": "deleteNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/watch/nodes", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.MinionList", + "method": "GET", + "summary": "watch a list of Node", + "nickname": "watchNodelist", + "parameters": [], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/watch/replicationControllers/{name}", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.ReplicationController", + "method": "GET", + "summary": "watch a particular ReplicationController", + "nickname": "watchReplicationController", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the ReplicationController", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/watch/pods/{name}", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.Pod", + "method": "GET", + "summary": "watch a particular Pod", + "nickname": "watchPod", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Pod", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/proxy/pods/{name}/{path:*}", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "void", + "method": "GET", + "summary": "proxy GET requests to Pod", + "nickname": "proxyGETPod", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Pod", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "proxy PUT requests to Pod", + "nickname": "proxyPUTPod", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Pod", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "proxy POST requests to Pod", + "nickname": "proxyPOSTPod", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Pod", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "proxy DELETE requests to Pod", + "nickname": "proxyDELETEPod", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Pod", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/watch/minions", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.MinionList", + "method": "GET", + "summary": "watch a list of Node", + "nickname": "watchNodelist", + "parameters": [], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/watch/services", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.Service", + "method": "GET", + "summary": "watch a list of Service", + "nickname": "watchServicelist", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/services/{name}", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.Service", + "method": "GET", + "summary": "read the specified Service", + "nickname": "readService", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Service", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "update the specified Service", + "nickname": "updateService", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Service", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta2.Service", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "delete a Service", + "nickname": "deleteService", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Service", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/endpoints/{name}", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.Endpoints", + "method": "GET", + "summary": "read the specified Endpoints", + "nickname": "readEndpoints", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Endpoints", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "update the specified Endpoints", + "nickname": "updateEndpoints", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Endpoints", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta2.Endpoints", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/watch/pods", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.PodList", + "method": "GET", + "summary": "watch a list of Pod", + "nickname": "watchPodlist", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/events", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.EventList", + "method": "GET", + "summary": "list objects of kind Event", + "nickname": "listEvent", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "create a Event", + "nickname": "createEvent", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta2.Event", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/watch/events/{name}", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.Event", + "method": "GET", + "summary": "watch a particular Event", + "nickname": "watchEvent", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Event", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/resourceQuotaUsages", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "void", + "method": "POST", + "summary": "create a ResourceQuotaUsage", + "nickname": "createResourceQuotaUsage", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta2.ResourceQuotaUsage", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta2/watch/endpoints/{name}", + "description": "API at /api/v1beta2 version v1beta2", + "operations": [ + { + "type": "v1beta2.Endpoints", + "method": "GET", + "summary": "watch a particular Endpoints", + "nickname": "watchEndpoints", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Endpoints", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": false, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + } + ], + "models": { + "v1beta2.Binding": { + "id": "v1beta2.Binding", + "required": [ + "podID", + "host" + ], + "properties": { + "annotations": { + "type": "v1beta2.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "host": { + "type": "string", + "description": "host to which to bind the specified pod" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "podID": { + "type": "string", + "description": "name of the pod to bind" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta2.Capabilities": { + "id": "v1beta2.Capabilities", + "properties": { + "add": { + "type": "array", + "items": [ + { + "$ref": "v1beta2.CapabilityType" + } + ], + "description": "added capabilities" + }, + "drop": { + "type": "array", + "items": [ + { + "$ref": "v1beta2.CapabilityType" + } + ], + "description": "droped capabilities" + } + } + }, + "v1beta2.CapabilityType": { + "id": "v1beta2.CapabilityType", + "properties": {} + }, + "v1beta2.Container": { + "id": "v1beta2.Container", + "required": [ + "name", + "image", + "imagePullPolicy" + ], + "properties": { + "capabilities": { + "type": "v1beta2.Capabilities", + "description": "capabilities for container" + }, + "command": { + "type": "array", + "items": [ + { + "$ref": "string" + } + ], + "description": "command argv array; not executed within a shell; defaults to entrypoint or command in the image" + }, + "cpu": { + "type": "integer", + "format": "int32", + "description": "CPU share in thousandths of a core" + }, + "env": { + "type": "array", + "items": [ + { + "$ref": "v1beta2.EnvVar" + } + ], + "description": "list of environment variables to set in the container" + }, + "image": { + "type": "string", + "description": "Docker image name" + }, + "imagePullPolicy": { + "type": "v1beta2.PullPolicy", + "description": "image pull policy; one of PullAlways, PullNever, PullIfNotPresent; defaults to PullAlways if :latest tag is specified, or PullIfNotPresent otherwise" + }, + "lifecycle": { + "type": "v1beta2.Lifecycle", + "description": "actions that the management system should take in response to container lifecycle events" + }, + "livenessProbe": { + "type": "v1beta2.LivenessProbe", + "description": "periodic probe of container liveness; container will be restarted if the probe fails" + }, + "memory": { + "type": "integer", + "format": "int64", + "description": "memory limit in bytes; defaults to unlimited" + }, + "name": { + "type": "string", + "description": "name of the container; must be a DNS_LABEL and unique within the pod" + }, + "ports": { + "type": "array", + "items": [ + { + "$ref": "v1beta2.Port" + } + ], + "description": "list of ports to expose from the container" + }, + "privileged": { + "type": "boolean", + "description": "whether or not the container is granted privileged status; defaults to false" + }, + "resources": { + "type": "v1beta2.ResourceRequirementSpec", + "description": "Compute Resources required by this container" + }, + "terminationMessagePath": { + "type": "string", + "description": "path at which the file to which the container's termination message will be written is mounted into the container's filesystem; message written is intended to be brief final status, such as an assertion failure message; defaults to /dev/termination-log" + }, + "volumeMounts": { + "type": "array", + "items": [ + { + "$ref": "v1beta2.VolumeMount" + } + ], + "description": "pod volumes to mount into the container's filesystem" + }, + "workingDir": { + "type": "string", + "description": "container's working directory; defaults to image's default" + } + } + }, + "v1beta2.ContainerManifest": { + "id": "v1beta2.ContainerManifest", + "required": [ + "version", + "id", + "volumes", + "containers" + ], + "properties": { + "containers": { + "type": "array", + "items": [ + { + "$ref": "v1beta2.Container" + } + ], + "description": "list of containers belonging to the pod" + }, + "dnsPolicy": { + "type": "v1beta2.DNSPolicy", + "description": "DNS policy for containers within the pod; one of 'ClusterFirst' or 'Default'" + }, + "id": { + "type": "string", + "description": "manifest name; must be a DNS_SUBDOMAIN" + }, + "restartPolicy": { + "type": "v1beta2.RestartPolicy", + "description": "restart policy for all containers within the pod; one of RestartPolicyAlways, RestartPolicyOnFailure, RestartPolicyNever" + }, + "uuid": { + "type": "types.UID", + "description": "manifest UUID" + }, + "version": { + "type": "string", + "description": "manifest version; must be v1beta1" + }, + "volumes": { + "type": "array", + "items": [ + { + "$ref": "v1beta2.Volume" + } + ], + "description": "list of volumes that can be mounted by containers belonging to the pod" + } + } + }, + "v1beta2.EmptyDir": { + "id": "v1beta2.EmptyDir", + "properties": {} + }, + "v1beta2.Endpoints": { + "id": "v1beta2.Endpoints", + "properties": { + "annotations": { + "type": "v1beta2.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "endpoints": { + "type": "array", + "items": [ + { + "$ref": "string" + } + ], + "description": "list of endpoints corresponding to a service, of the form address:port, such as 10.10.1.1:1909" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta2.EndpointsList": { + "id": "v1beta2.EndpointsList", + "required": [ + "items" + ], + "properties": { + "annotations": { + "type": "v1beta2.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "items": { + "type": "array", + "items": [ + { + "$ref": "v1beta2.Endpoints" + } + ], + "description": "list of service endpoint lists" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta2.EnvVar": { + "id": "v1beta2.EnvVar", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "description": "name of the environment variable; must be a C_IDENTIFIER" + }, + "value": { + "type": "string", + "description": "value of the environment variable; defaults to empty string" + } + } + }, + "v1beta2.Event": { + "id": "v1beta2.Event", + "properties": { + "annotations": { + "type": "v1beta2.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "host": { + "type": "string", + "description": "host name on which this event was generated" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "involvedObject": { + "type": "v1beta2.ObjectReference", + "description": "object that this event is about" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "message": { + "type": "string", + "description": "human-readable description of the status of this operation" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "reason": { + "type": "string", + "description": "short, machine understandable string that gives the reason for the transition into the object's current status" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "source": { + "type": "string", + "description": "component reporting this event; short machine understandable string" + }, + "status": { + "type": "string", + "description": "short, machine understandable string that describes the current status of the referred object" + }, + "timestamp": { + "type": "string", + "description": "time at which the client recorded the event" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta2.EventList": { + "id": "v1beta2.EventList", + "required": [ + "items" + ], + "properties": { + "annotations": { + "type": "v1beta2.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "items": { + "type": "array", + "items": [ + { + "$ref": "v1beta2.Event" + } + ], + "description": "list of events" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta2.ExecAction": { + "id": "v1beta2.ExecAction", + "properties": { + "command": { + "type": "array", + "items": [ + { + "$ref": "string" + } + ], + "description": "command line to execute inside the container; working directory for the command is root ('/') in the container's file system; the command is exec'd, not run inside a shell; exit status of 0 is treated as live/healthy and non-zero is unhealthy" + } + } + }, + "v1beta2.GCEPersistentDisk": { + "id": "v1beta2.GCEPersistentDisk", + "required": [ + "pdName" + ], + "properties": { + "fsType": { + "type": "string", + "description": "file system type to mount, such as ext4, xfs, ntfs" + }, + "partition": { + "type": "integer", + "format": "int32", + "description": "partition on the disk to mount (e.g., '1' for /dev/sda1); if omitted the plain device name (e.g., /dev/sda) will be mounted" + }, + "pdName": { + "type": "string", + "description": "unique name of the PD resource in GCE" + }, + "readOnly": { + "type": "boolean", + "description": "read-only if true, read-write otherwise (false or unspecified)" + } + } + }, + "v1beta2.GitRepo": { + "id": "v1beta2.GitRepo", + "required": [ + "repository", + "revision" + ], + "properties": { + "repository": { + "type": "string", + "description": "repository URL" + }, + "revision": { + "type": "string", + "description": "commit hash for the specified revision" + } + } + }, + "v1beta2.HTTPGetAction": { + "id": "v1beta2.HTTPGetAction", + "properties": { + "host": { + "type": "string", + "description": "hostname to connect to; defaults to pod IP" + }, + "path": { + "type": "string", + "description": "path to access on the HTTP server" + }, + "port": { + "type": "string", + "description": "number or name of the port to access on the container" + } + } + }, + "v1beta2.Handler": { + "id": "v1beta2.Handler", + "properties": { + "exec": { + "type": "v1beta2.ExecAction", + "description": "exec-based handler" + }, + "httpGet": { + "type": "v1beta2.HTTPGetAction", + "description": "HTTP-based handler" + }, + "tcpSocket": { + "type": "v1beta2.TCPSocketAction", + "description": "TCP-based handler; TCP hooks not yet supported" + } + } + }, + "v1beta2.HostPath": { + "id": "v1beta2.HostPath", + "required": [ + "path" + ], + "properties": { + "path": { + "type": "string", + "description": "path of the directory on the host" + } + } + }, + "v1beta2.Lifecycle": { + "id": "v1beta2.Lifecycle", + "properties": { + "postStart": { + "type": "v1beta2.Handler", + "description": "called immediately after a container is started; if the handler fails, the container is terminated and restarted according to its restart policy; other management of the container blocks until the hook completes" + }, + "preStop": { + "type": "v1beta2.Handler", + "description": "called before a container is terminated; the container is terminated after the handler completes; other management of the container blocks until the hook completes" + } + } + }, + "v1beta2.LimitRange": { + "id": "v1beta2.LimitRange", + "properties": { + "annotations": { + "type": "v1beta2.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "spec": { + "type": "v1beta2.LimitRangeSpec" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta2.LimitRangeItem": { + "id": "v1beta2.LimitRangeItem", + "properties": { + "max": { + "type": "v1beta2.ResourceList" + }, + "min": { + "type": "v1beta2.ResourceList" + }, + "type": { + "type": "v1beta2.LimitType" + } + } + }, + "v1beta2.LimitRangeList": { + "id": "v1beta2.LimitRangeList", + "required": [ + "items" + ], + "properties": { + "annotations": { + "type": "v1beta2.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "items": { + "type": "array", + "items": [ + { + "$ref": "v1beta2.LimitRange" + } + ] + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta2.LimitRangeSpec": { + "id": "v1beta2.LimitRangeSpec", + "required": [ + "limits" + ], + "properties": { + "limits": { + "type": "array", + "items": [ + { + "$ref": "v1beta2.LimitRangeItem" + } + ] + } + } + }, + "v1beta2.LivenessProbe": { + "id": "v1beta2.LivenessProbe", + "properties": { + "exec": { + "type": "v1beta2.ExecAction", + "description": "parameters for exec-based liveness probe" + }, + "httpGet": { + "type": "v1beta2.HTTPGetAction", + "description": "parameters for HTTP-based liveness probe" + }, + "initialDelaySeconds": { + "type": "integer", + "format": "int64", + "description": "number of seconds after the container has started before liveness probes are initiated" + }, + "tcpSocket": { + "type": "v1beta2.TCPSocketAction", + "description": "parameters for TCP-based liveness probe" + } + } + }, + "v1beta2.Minion": { + "id": "v1beta2.Minion", + "properties": { + "annotations": { + "type": "v1beta2.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "hostIP": { + "type": "string", + "description": "IP address of the node" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "labels": { + "type": "v1beta2.Minion.labels", + "description": "map of string keys and values that can be used to organize and categorize minions; labels of a minion assigned by the scheduler must match the scheduled pod's nodeSelector" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "resources": { + "type": "v1beta2.NodeResources", + "description": "characterization of node resources" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "status": { + "type": "v1beta2.NodeStatus", + "description": "current status of node" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta2.Minion.labels": { + "id": "v1beta2.Minion.labels", + "properties": {} + }, + "v1beta2.MinionList": { + "id": "v1beta2.MinionList", + "required": [ + "items" + ], + "properties": { + "annotations": { + "type": "v1beta2.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "items": { + "type": "array", + "items": [ + { + "$ref": "v1beta2.Minion" + } + ], + "description": "list of nodes" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta2.NodeCondition": { + "id": "v1beta2.NodeCondition", + "required": [ + "kind", + "status" + ], + "properties": { + "kind": { + "type": "v1beta2.NodeConditionKind", + "description": "kind of the condition, one of reachable, ready" + }, + "lastTransitionTime": { + "type": "string", + "description": "last time the condition transit from one status to another" + }, + "message": { + "type": "string", + "description": "human readable message indicating details about last transition" + }, + "reason": { + "type": "string", + "description": "(brief) reason for the condition's last transition" + }, + "status": { + "type": "v1beta2.NodeConditionStatus", + "description": "status of the condition, one of full, none, unknown" + } + } + }, + "v1beta2.NodeResources": { + "id": "v1beta2.NodeResources", + "properties": { + "capacity": { + "type": "v1beta2.ResourceList", + "description": "resource capacity of a node represented as a map of resource name to quantity of resource" + } + } + }, + "v1beta2.NodeStatus": { + "id": "v1beta2.NodeStatus", + "properties": { + "conditions": { + "type": "array", + "items": [ + { + "$ref": "v1beta2.NodeCondition" + } + ], + "description": "conditions is an array of current node conditions" + }, + "phase": { + "type": "v1beta2.NodePhase", + "description": "node phase is the current lifecycle phase of the node" + } + } + }, + "v1beta2.ObjectReference": { + "id": "v1beta2.ObjectReference", + "properties": { + "apiVersion": { + "type": "string", + "description": "API version of the referent" + }, + "fieldPath": { + "type": "string", + "description": "if referring to a piece of an object instead of an entire object, this string should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]" + }, + "kind": { + "type": "string", + "description": "kind of the referent" + }, + "name": { + "type": "string", + "description": "id of the referent" + }, + "namespace": { + "type": "string", + "description": "namespace of the referent" + }, + "resourceVersion": { + "type": "string", + "description": "specific resourceVersion to which this reference is made, if any" + }, + "uid": { + "type": "types.UID", + "description": "uid of the referent" + } + } + }, + "v1beta2.Pod": { + "id": "v1beta2.Pod", + "properties": { + "annotations": { + "type": "v1beta2.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "currentState": { + "type": "v1beta2.PodState", + "description": "current state of the pod" + }, + "desiredState": { + "type": "v1beta2.PodState", + "description": "specification of the desired state of the pod" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "labels": { + "type": "v1beta2.Pod.labels", + "description": "map of string keys and values that can be used to organize and categorize pods; may match selectors of replication controllers and services" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "nodeSelector": { + "type": "v1beta2.Pod.nodeSelector", + "description": "selector which must match a node's labels for the pod to be scheduled on that node" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta2.Pod.labels": { + "id": "v1beta2.Pod.labels", + "properties": {} + }, + "v1beta2.Pod.nodeSelector": { + "id": "v1beta2.Pod.nodeSelector", + "properties": {} + }, + "v1beta2.PodList": { + "id": "v1beta2.PodList", + "required": [ + "items" + ], + "properties": { + "annotations": { + "type": "v1beta2.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "items": { + "type": "array", + "items": [ + { + "$ref": "v1beta2.Pod" + } + ], + "description": "list of pods" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta2.PodState": { + "id": "v1beta2.PodState", + "properties": { + "host": { + "type": "string", + "description": "host to which the pod is assigned; empty if not yet scheduled" + }, + "hostIP": { + "type": "string", + "description": "IP address of the host to which the pod is assigned; empty if not yet scheduled" + }, + "info": { + "type": "v1beta2.PodInfo", + "description": "map of container name to container status" + }, + "manifest": { + "type": "v1beta2.ContainerManifest", + "description": "manifest of containers and volumes comprising the pod" + }, + "message": { + "type": "string", + "description": "human readable message indicating details about why the pod is in this condition" + }, + "podIP": { + "type": "string", + "description": "IP address allocated to the pod; routable at least within the cluster; empty if not yet allocated" + }, + "status": { + "type": "v1beta2.PodStatus", + "description": "current condition of the pod, Waiting, Running, or Terminated" + } + } + }, + "v1beta2.PodTemplate": { + "id": "v1beta2.PodTemplate", + "properties": { + "annotations": { + "type": "v1beta2.PodTemplate.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about pods created from the template" + }, + "desiredState": { + "type": "v1beta2.PodState", + "description": "specification of the desired state of pods created from this template" + }, + "labels": { + "type": "v1beta2.PodTemplate.labels", + "description": "map of string keys and values that can be used to organize and categorize the pods created from the template; must match the selector of the replication controller to which the template belongs; may match selectors of services" + }, + "nodeSelector": { + "type": "v1beta2.PodTemplate.nodeSelector", + "description": "a selector which must be true for the pod to fit on a node" + } + } + }, + "v1beta2.PodTemplate.annotations": { + "id": "v1beta2.PodTemplate.annotations", + "properties": {} + }, + "v1beta2.PodTemplate.labels": { + "id": "v1beta2.PodTemplate.labels", + "properties": {} + }, + "v1beta2.PodTemplate.nodeSelector": { + "id": "v1beta2.PodTemplate.nodeSelector", + "properties": {} + }, + "v1beta2.Port": { + "id": "v1beta2.Port", + "required": [ + "containerPort" + ], + "properties": { + "containerPort": { + "type": "integer", + "format": "int32", + "description": "number of port to expose on the pod's IP address" + }, + "hostIP": { + "type": "string", + "description": "host IP to bind the port to" + }, + "hostPort": { + "type": "integer", + "format": "int32", + "description": "number of port to expose on the host; most containers do not need this" + }, + "name": { + "type": "string", + "description": "name for the port that can be referred to by services; must be a DNS_LABEL and unique without the pod" + }, + "protocol": { + "type": "v1beta2.Protocol", + "description": "protocol for port; must be UDP or TCP; TCP if unspecified" + } + } + }, + "v1beta2.ReplicationController": { + "id": "v1beta2.ReplicationController", + "properties": { + "annotations": { + "type": "v1beta2.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "currentState": { + "type": "v1beta2.ReplicationControllerState", + "description": "current state of the replication controller" + }, + "desiredState": { + "type": "v1beta2.ReplicationControllerState", + "description": "specification of the desired state of the replication controller" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "labels": { + "type": "v1beta2.ReplicationController.labels", + "description": "map of string keys and values that can be used to organize and categorize replication controllers" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta2.ReplicationController.labels": { + "id": "v1beta2.ReplicationController.labels", + "properties": {} + }, + "v1beta2.ReplicationControllerList": { + "id": "v1beta2.ReplicationControllerList", + "required": [ + "items" + ], + "properties": { + "annotations": { + "type": "v1beta2.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "items": { + "type": "array", + "items": [ + { + "$ref": "v1beta2.ReplicationController" + } + ], + "description": "list of replication controllers" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta2.ReplicationControllerState": { + "id": "v1beta2.ReplicationControllerState", + "required": [ + "replicas" + ], + "properties": { + "podTemplate": { + "type": "v1beta2.PodTemplate", + "description": "template for pods to be created by this replication controller when the observed number of replicas is less than the desired number of replicas" + }, + "replicaSelector": { + "type": "v1beta2.ReplicationControllerState.replicaSelector", + "description": "label keys and values that must match in order to be controlled by this replication controller" + }, + "replicas": { + "type": "integer", + "format": "int32", + "description": "number of replicas (desired or observed, as appropriate)" + } + } + }, + "v1beta2.ReplicationControllerState.replicaSelector": { + "id": "v1beta2.ReplicationControllerState.replicaSelector", + "properties": {} + }, + "v1beta2.ResourceQuota": { + "id": "v1beta2.ResourceQuota", + "properties": { + "annotations": { + "type": "v1beta2.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "spec": { + "type": "v1beta2.ResourceQuotaSpec" + }, + "status": { + "type": "v1beta2.ResourceQuotaStatus" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta2.ResourceQuotaList": { + "id": "v1beta2.ResourceQuotaList", + "required": [ + "items" + ], + "properties": { + "annotations": { + "type": "v1beta2.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "items": { + "type": "array", + "items": [ + { + "$ref": "v1beta2.ResourceQuota" + } + ] + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta2.ResourceQuotaSpec": { + "id": "v1beta2.ResourceQuotaSpec", + "properties": { + "hard": { + "type": "v1beta2.ResourceList" + } + } + }, + "v1beta2.ResourceQuotaStatus": { + "id": "v1beta2.ResourceQuotaStatus", + "properties": { + "hard": { + "type": "v1beta2.ResourceList" + }, + "used": { + "type": "v1beta2.ResourceList" + } + } + }, + "v1beta2.ResourceQuotaUsage": { + "id": "v1beta2.ResourceQuotaUsage", + "properties": { + "annotations": { + "type": "v1beta2.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "status": { + "type": "v1beta2.ResourceQuotaStatus" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta2.ResourceRequirementSpec": { + "id": "v1beta2.ResourceRequirementSpec", + "properties": { + "limits": { + "type": "v1beta2.ResourceList", + "description": "Maximum amount of compute resources allowed" + } + } + }, + "v1beta2.RestartPolicy": { + "id": "v1beta2.RestartPolicy", + "properties": { + "always": { + "type": "v1beta2.RestartPolicyAlways", + "description": "always restart the container after termination" + }, + "never": { + "type": "v1beta2.RestartPolicyNever", + "description": "never restart the container" + }, + "onFailure": { + "type": "v1beta2.RestartPolicyOnFailure", + "description": "restart the container if it fails for any reason, but not if it succeeds (exit 0)" + } + } + }, + "v1beta2.RestartPolicyAlways": { + "id": "v1beta2.RestartPolicyAlways", + "properties": {} + }, + "v1beta2.RestartPolicyNever": { + "id": "v1beta2.RestartPolicyNever", + "properties": {} + }, + "v1beta2.RestartPolicyOnFailure": { + "id": "v1beta2.RestartPolicyOnFailure", + "properties": {} + }, + "v1beta2.Service": { + "id": "v1beta2.Service", + "required": [ + "port", + "selector" + ], + "properties": { + "annotations": { + "type": "v1beta2.TypeMeta.annotations", + "description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about the object" + }, + "apiVersion": { + "type": "string", + "description": "version of the schema the object should have" + }, + "containerPort": { + "type": "string", + "description": "number or name of the port to access on the containers belonging to pods targeted by the service" + }, + "createExternalLoadBalancer": { + "type": "boolean", + "description": "set up a cloud-provider-specific load balancer on an external IP" + }, + "creationTimestamp": { + "type": "string", + "description": "RFC 3339 date and time at which the object was created; recorded by the system; null for lists" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "id": { + "type": "string", + "description": "name of the object; must be a DNS_SUBDOMAIN and unique among all objects of the same kind within the same namespace; used in resource URLs" + }, + "kind": { + "type": "string", + "description": "kind of object, in CamelCase" + }, + "labels": { + "type": "v1beta2.Service.labels", + "description": "map of string keys and values that can be used to organize and categorize services" + }, + "namespace": { + "type": "string", + "description": "namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default" + }, + "port": { + "type": "integer", + "format": "int32", + "description": "port exposed by the service" + }, + "portalIP": { + "type": "string", + "description": "IP address of the service; usually assigned by the system; if specified, it will be allocated to the service if unused, and creation of the service will fail otherwise; cannot be updated" + }, + "protocol": { + "type": "v1beta2.Protocol", + "description": "protocol for port; must be UDP or TCP; TCP if unspecified" + }, + "proxyPort": { + "type": "integer", + "format": "int32", + "description": "if non-zero, a pre-allocated host port used for this service by the proxy on each node; assigned by the master and ignored on input" + }, + "publicIPs": { + "type": "array", + "items": [ + { + "$ref": "string" + } + ], + "description": "externally visible IPs from which to select the address for the external load balancer" + }, + "resourceVersion": { + "type": "uint64", + "description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; value must be treated as opaque by clients and passed unmodified back to the server" + }, + "selector": { + "type": "v1beta2.Service.selector", + "description": "label keys and values that must match in order to receive traffic for this service; if empty, all pods are selected, if not specified, endpoints must be manually specified" + }, + "selfLink": { + "type": "string", + "description": "URL for the object" + }, + "sessionAffinity": { + "type": "v1beta2.AffinityType", + "description": "enable client IP based session affinity; must be ClientIP or None; defaults to None" + }, + "uid": { + "type": "types.UID", + "description": "UUID assigned by the system upon creation, unique across space and time" + } + } + }, + "v1beta2.Service.labels": { + "id": "v1beta2.Service.labels", + "properties": {} + }, + "v1beta2.Service.selector": { + "id": "v1beta2.Service.selector", + "properties": {} + }, + "v1beta2.TCPSocketAction": { + "id": "v1beta2.TCPSocketAction", + "properties": { + "port": { + "type": "string", + "description": "number of name of the port to access on the container" + } + } + }, + "v1beta2.Volume": { + "id": "v1beta2.Volume", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "description": "volume name; must be a DNS_LABEL and unique within the pod" + }, + "source": { + "type": "v1beta2.VolumeSource", + "description": "location and type of volume to mount; at most one of HostDir, EmptyDir, GCEPersistentDisk, or GitRepo; default is EmptyDir" + } + } + }, + "v1beta2.VolumeMount": { + "id": "v1beta2.VolumeMount", + "required": [ + "name", + "mountPath" + ], + "properties": { + "mountPath": { + "type": "string", + "description": "path within the container at which the volume should be mounted" + }, + "name": { + "type": "string", + "description": "name of the volume to mount" + }, + "readOnly": { + "type": "boolean", + "description": "mounted read-only if true, read-write otherwise (false or unspecified)" + } + } + }, + "v1beta2.VolumeSource": { + "id": "v1beta2.VolumeSource", + "required": [ + "hostDir", + "emptyDir", + "persistentDisk", + "gitRepo" + ], + "properties": { + "emptyDir": { + "type": "v1beta2.EmptyDir", + "description": "temporary directory that shares a pod's lifetime" + }, + "gitRepo": { + "type": "v1beta2.GitRepo", + "description": "git repository at a particular revision" + }, + "hostDir": { + "type": "v1beta2.HostPath", + "description": "pre-existing host file or directory; generally for privileged system daemons or other agents tied to the host" + }, + "persistentDisk": { + "type": "v1beta2.GCEPersistentDisk", + "description": "GCE disk resource attached to the host machine on demand" + } + } + } + } + } \ No newline at end of file diff --git a/api/swagger-spec/v1beta3.json b/api/swagger-spec/v1beta3.json new file mode 100644 index 00000000000..8743266e539 --- /dev/null +++ b/api/swagger-spec/v1beta3.json @@ -0,0 +1,3812 @@ +{ + "swaggerVersion": "1.2", + "apiVersion": "v1beta3", + "basePath": "127.0.0.1:8050", + "resourcePath": "/api/v1beta3", + "apis": [ + { + "path": "/api/v1beta3/resourcequotas", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.ResourceQuotaList", + "method": "GET", + "summary": "list objects of kind ResourceQuota", + "nickname": "listResourceQuota", + "parameters": [], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/ns/{ns}/pods", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.PodList", + "method": "GET", + "summary": "list objects of kind Pod", + "nickname": "listPod", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "create a Pod", + "nickname": "createPod", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + }, + { + "type": "v1beta3.Pod", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/services", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.Service", + "method": "GET", + "summary": "list objects of kind Service", + "nickname": "listService", + "parameters": [], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/proxy/nodes/{name}/{path:*}", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "void", + "method": "GET", + "summary": "proxy GET requests to Node", + "nickname": "proxyGETNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "proxy PUT requests to Node", + "nickname": "proxyPUTNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "proxy POST requests to Node", + "nickname": "proxyPOSTNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "proxy DELETE requests to Node", + "nickname": "proxyDELETENode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/watch/ns/{ns}/replicationcontrollers", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.ReplicationControllerList", + "method": "GET", + "summary": "watch a list of ReplicationController", + "nickname": "watchReplicationControllerlist", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/ns/{ns}/services", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.Service", + "method": "GET", + "summary": "list objects of kind Service", + "nickname": "listService", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "create a Service", + "nickname": "createService", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + }, + { + "type": "v1beta3.Service", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/ns/{ns}/limitranges", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.LimitRangeList", + "method": "GET", + "summary": "list objects of kind LimitRange", + "nickname": "listLimitRange", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "create a LimitRange", + "nickname": "createLimitRange", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + }, + { + "type": "v1beta3.LimitRange", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/ns/{ns}/events", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.EventList", + "method": "GET", + "summary": "list objects of kind Event", + "nickname": "listEvent", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "create a Event", + "nickname": "createEvent", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + }, + { + "type": "v1beta3.Event", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/watch/ns/{ns}/events/{name}", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.Event", + "method": "GET", + "summary": "watch a particular Event", + "nickname": "watchEvent", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Event", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/watch/ns/{ns}/resourcequotas", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.ResourceQuotaList", + "method": "GET", + "summary": "watch a list of ResourceQuota", + "nickname": "watchResourceQuotalist", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/ns/{ns}/resourcequotausages", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "void", + "method": "POST", + "summary": "create a ResourceQuotaUsage", + "nickname": "createResourceQuotaUsage", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + }, + { + "type": "v1beta3.ResourceQuotaUsage", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/watch/endpoints", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.EndpointsList", + "method": "GET", + "summary": "watch a list of Endpoints", + "nickname": "watchEndpointslist", + "parameters": [], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/watch/ns/{ns}/pods", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.PodList", + "method": "GET", + "summary": "watch a list of Pod", + "nickname": "watchPodlist", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/replicationcontrollers", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.ReplicationControllerList", + "method": "GET", + "summary": "list objects of kind ReplicationController", + "nickname": "listReplicationController", + "parameters": [], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/watch/replicationcontrollers", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.ReplicationControllerList", + "method": "GET", + "summary": "watch a list of ReplicationController", + "nickname": "watchReplicationControllerlist", + "parameters": [], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/watch/ns/{ns}/limitranges/{name}", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.LimitRange", + "method": "GET", + "summary": "watch a particular LimitRange", + "nickname": "watchLimitRange", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the LimitRange", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/limitranges", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.LimitRangeList", + "method": "GET", + "summary": "list objects of kind LimitRange", + "nickname": "listLimitRange", + "parameters": [], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/nodes/{name}", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.Node", + "method": "GET", + "summary": "read the specified Node", + "nickname": "readNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "update the specified Node", + "nickname": "updateNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + }, + { + "type": "v1beta3.Node", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "delete a Node", + "nickname": "deleteNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/watch/nodes/{name}", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.Node", + "method": "GET", + "summary": "watch a particular Node", + "nickname": "watchNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/watch/ns/{ns}/events", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.EventList", + "method": "GET", + "summary": "watch a list of Event", + "nickname": "watchEventlist", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/ns/{ns}/events/{name}", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.Event", + "method": "GET", + "summary": "read the specified Event", + "nickname": "readEvent", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Event", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "delete a Event", + "nickname": "deleteEvent", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Event", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/ns/{ns}/endpoints/{name}", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.Endpoints", + "method": "GET", + "summary": "read the specified Endpoints", + "nickname": "readEndpoints", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Endpoints", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "update the specified Endpoints", + "nickname": "updateEndpoints", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Endpoints", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + }, + { + "type": "v1beta3.Endpoints", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/pods", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.PodList", + "method": "GET", + "summary": "list objects of kind Pod", + "nickname": "listPod", + "parameters": [], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/ns/{ns}/services/{name}", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.Service", + "method": "GET", + "summary": "read the specified Service", + "nickname": "readService", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Service", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "update the specified Service", + "nickname": "updateService", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Service", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + }, + { + "type": "v1beta3.Service", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "delete a Service", + "nickname": "deleteService", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Service", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/events", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.EventList", + "method": "GET", + "summary": "list objects of kind Event", + "nickname": "listEvent", + "parameters": [], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/watch/ns/{ns}/endpoints", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.EndpointsList", + "method": "GET", + "summary": "watch a list of Endpoints", + "nickname": "watchEndpointslist", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/watch/nodes", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.NodeList", + "method": "GET", + "summary": "watch a list of Node", + "nickname": "watchNodelist", + "parameters": [], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/ns/{ns}/resourcequotas", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.ResourceQuotaList", + "method": "GET", + "summary": "list objects of kind ResourceQuota", + "nickname": "listResourceQuota", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "create a ResourceQuota", + "nickname": "createResourceQuota", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + }, + { + "type": "v1beta3.ResourceQuota", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/ns/{ns}/bindings", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "void", + "method": "POST", + "summary": "create a Binding", + "nickname": "createBinding", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + }, + { + "type": "v1beta3.Binding", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/watch/services", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.Service", + "method": "GET", + "summary": "watch a list of Service", + "nickname": "watchServicelist", + "parameters": [], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/watch/ns/{ns}/limitranges", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.LimitRangeList", + "method": "GET", + "summary": "watch a list of LimitRange", + "nickname": "watchLimitRangelist", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/ns/{ns}/limitranges/{name}", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.LimitRange", + "method": "GET", + "summary": "read the specified LimitRange", + "nickname": "readLimitRange", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the LimitRange", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "update the specified LimitRange", + "nickname": "updateLimitRange", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the LimitRange", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + }, + { + "type": "v1beta3.LimitRange", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "delete a LimitRange", + "nickname": "deleteLimitRange", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the LimitRange", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/ns/{ns}/resourcequotas/{name}", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.ResourceQuota", + "method": "GET", + "summary": "read the specified ResourceQuota", + "nickname": "readResourceQuota", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the ResourceQuota", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "update the specified ResourceQuota", + "nickname": "updateResourceQuota", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the ResourceQuota", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + }, + { + "type": "v1beta3.ResourceQuota", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "delete a ResourceQuota", + "nickname": "deleteResourceQuota", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the ResourceQuota", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/ns/{ns}/pods/{name}", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.Pod", + "method": "GET", + "summary": "read the specified Pod", + "nickname": "readPod", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Pod", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "update the specified Pod", + "nickname": "updatePod", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Pod", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + }, + { + "type": "v1beta3.Pod", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "delete a Pod", + "nickname": "deletePod", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Pod", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/watch/ns/{ns}/pods/{name}", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.Pod", + "method": "GET", + "summary": "watch a particular Pod", + "nickname": "watchPod", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Pod", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/proxy/ns/{ns}/pods/{name}/{path:*}", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "void", + "method": "GET", + "summary": "proxy GET requests to Pod", + "nickname": "proxyGETPod", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Pod", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "proxy PUT requests to Pod", + "nickname": "proxyPUTPod", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Pod", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "proxy POST requests to Pod", + "nickname": "proxyPOSTPod", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Pod", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "proxy DELETE requests to Pod", + "nickname": "proxyDELETEPod", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Pod", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/endpoints", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.EndpointsList", + "method": "GET", + "summary": "list objects of kind Endpoints", + "nickname": "listEndpoints", + "parameters": [], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/watch/resourcequotas", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.ResourceQuotaList", + "method": "GET", + "summary": "watch a list of ResourceQuota", + "nickname": "watchResourceQuotalist", + "parameters": [], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/ns/{ns}/replicationcontrollers", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.ReplicationControllerList", + "method": "GET", + "summary": "list objects of kind ReplicationController", + "nickname": "listReplicationController", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "create a ReplicationController", + "nickname": "createReplicationController", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + }, + { + "type": "v1beta3.ReplicationController", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/watch/ns/{ns}/replicationcontrollers/{name}", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.ReplicationController", + "method": "GET", + "summary": "watch a particular ReplicationController", + "nickname": "watchReplicationController", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the ReplicationController", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/watch/ns/{ns}/services", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.Service", + "method": "GET", + "summary": "watch a list of Service", + "nickname": "watchServicelist", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/watch/ns/{ns}/resourcequotas/{name}", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.ResourceQuota", + "method": "GET", + "summary": "watch a particular ResourceQuota", + "nickname": "watchResourceQuota", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the ResourceQuota", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/watch/ns/{ns}/services/{name}", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.Service", + "method": "GET", + "summary": "watch a particular Service", + "nickname": "watchService", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Service", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/watch/events", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.EventList", + "method": "GET", + "summary": "watch a list of Event", + "nickname": "watchEventlist", + "parameters": [], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/nodes", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.NodeList", + "method": "GET", + "summary": "list objects of kind Node", + "nickname": "listNode", + "parameters": [], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "create a Node", + "nickname": "createNode", + "parameters": [ + { + "type": "v1beta3.Node", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/proxy/ns/{ns}/services/{name}/{path:*}", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "void", + "method": "GET", + "summary": "proxy GET requests to Service", + "nickname": "proxyGETService", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Service", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "proxy PUT requests to Service", + "nickname": "proxyPUTService", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Service", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "proxy POST requests to Service", + "nickname": "proxyPOSTService", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Service", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "proxy DELETE requests to Service", + "nickname": "proxyDELETEService", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Service", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/ns/{ns}/endpoints", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.EndpointsList", + "method": "GET", + "summary": "list objects of kind Endpoints", + "nickname": "listEndpoints", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "POST", + "summary": "create a Endpoints", + "nickname": "createEndpoints", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + }, + { + "type": "v1beta3.Endpoints", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/watch/ns/{ns}/endpoints/{name}", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.Endpoints", + "method": "GET", + "summary": "watch a particular Endpoints", + "nickname": "watchEndpoints", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Endpoints", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/redirect/nodes/{name}", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "void", + "method": "GET", + "summary": "redirect GET request to Node", + "nickname": "redirectNode", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Node", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/watch/pods", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.PodList", + "method": "GET", + "summary": "watch a list of Pod", + "nickname": "watchPodlist", + "parameters": [], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/ns/{ns}/replicationcontrollers/{name}", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.ReplicationController", + "method": "GET", + "summary": "read the specified ReplicationController", + "nickname": "readReplicationController", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the ReplicationController", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "PUT", + "summary": "update the specified ReplicationController", + "nickname": "updateReplicationController", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the ReplicationController", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + }, + { + "type": "v1beta3.ReplicationController", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "void", + "method": "DELETE", + "summary": "delete a ReplicationController", + "nickname": "deleteReplicationController", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the ReplicationController", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/redirect/ns/{ns}/services/{name}", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "void", + "method": "GET", + "summary": "redirect GET request to Service", + "nickname": "redirectService", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Service", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/watch/limitranges", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "v1beta3.LimitRangeList", + "method": "GET", + "summary": "watch a list of LimitRange", + "nickname": "watchLimitRangelist", + "parameters": [], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/api/v1beta3/redirect/ns/{ns}/pods/{name}", + "description": "API at /api/v1beta3 version v1beta3", + "operations": [ + { + "type": "void", + "method": "GET", + "summary": "redirect GET request to Pod", + "nickname": "redirectPod", + "parameters": [ + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Pod", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "ns", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "produces": [ + "*/*" + ], + "consumes": [ + "*/*" + ] + } + ] + } + ], + "models": { + "v1beta3.Binding": { + "id": "v1beta3.Binding", + "required": [ + "podID", + "host" + ], + "properties": { + "annotations": { + "type": "v1beta3.ObjectMeta.annotations" + }, + "apiVersion": { + "type": "string" + }, + "creationTimestamp": { + "type": "string" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "host": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "labels": { + "type": "v1beta3.ObjectMeta.labels" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "podID": { + "type": "string" + }, + "resourceVersion": { + "type": "string" + }, + "selfLink": { + "type": "string" + }, + "uid": { + "type": "types.UID" + } + } + }, + "v1beta3.Capabilities": { + "id": "v1beta3.Capabilities", + "properties": { + "add": { + "type": "array", + "items": [ + { + "$ref": "v1beta3.CapabilityType" + } + ] + }, + "drop": { + "type": "array", + "items": [ + { + "$ref": "v1beta3.CapabilityType" + } + ] + } + } + }, + "v1beta3.CapabilityType": { + "id": "v1beta3.CapabilityType", + "properties": {} + }, + "v1beta3.Container": { + "id": "v1beta3.Container", + "required": [ + "name", + "image", + "imagePullPolicy" + ], + "properties": { + "capabilities": { + "type": "v1beta3.Capabilities" + }, + "command": { + "type": "array", + "items": [ + { + "$ref": "string" + } + ] + }, + "env": { + "type": "array", + "items": [ + { + "$ref": "v1beta3.EnvVar" + } + ] + }, + "image": { + "type": "string" + }, + "imagePullPolicy": { + "type": "v1beta3.PullPolicy" + }, + "lifecycle": { + "type": "v1beta3.Lifecycle" + }, + "livenessProbe": { + "type": "v1beta3.Probe" + }, + "name": { + "type": "string" + }, + "ports": { + "type": "array", + "items": [ + { + "$ref": "v1beta3.Port" + } + ] + }, + "privileged": { + "type": "boolean" + }, + "resources": { + "type": "v1beta3.ResourceRequirementSpec", + "description": "Compute Resources required by this container" + }, + "terminationMessagePath": { + "type": "string" + }, + "volumeMounts": { + "type": "array", + "items": [ + { + "$ref": "v1beta3.VolumeMount" + } + ] + }, + "workingDir": { + "type": "string" + } + } + }, + "v1beta3.EmptyDir": { + "id": "v1beta3.EmptyDir", + "properties": {} + }, + "v1beta3.Endpoints": { + "id": "v1beta3.Endpoints", + "required": [ + "endpoints" + ], + "properties": { + "annotations": { + "type": "v1beta3.ObjectMeta.annotations" + }, + "apiVersion": { + "type": "string" + }, + "creationTimestamp": { + "type": "string" + }, + "endpoints": { + "type": "array", + "items": [ + { + "$ref": "string" + } + ] + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "kind": { + "type": "string" + }, + "labels": { + "type": "v1beta3.ObjectMeta.labels" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "resourceVersion": { + "type": "string" + }, + "selfLink": { + "type": "string" + }, + "uid": { + "type": "types.UID" + } + } + }, + "v1beta3.EndpointsList": { + "id": "v1beta3.EndpointsList", + "required": [ + "items" + ], + "properties": { + "apiVersion": { + "type": "string" + }, + "items": { + "type": "array", + "items": [ + { + "$ref": "v1beta3.Endpoints" + } + ] + }, + "kind": { + "type": "string" + }, + "resourceVersion": { + "type": "string" + }, + "selfLink": { + "type": "string" + } + } + }, + "v1beta3.EnvVar": { + "id": "v1beta3.EnvVar", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, + "v1beta3.Event": { + "id": "v1beta3.Event", + "properties": { + "annotations": { + "type": "v1beta3.ObjectMeta.annotations" + }, + "apiVersion": { + "type": "string" + }, + "creationTimestamp": { + "type": "string" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "involvedObject": { + "type": "v1beta3.ObjectReference" + }, + "kind": { + "type": "string" + }, + "labels": { + "type": "v1beta3.ObjectMeta.labels" + }, + "message": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "resourceVersion": { + "type": "string" + }, + "selfLink": { + "type": "string" + }, + "source": { + "type": "v1beta3.EventSource" + }, + "timestamp": { + "type": "string" + }, + "uid": { + "type": "types.UID" + } + } + }, + "v1beta3.EventList": { + "id": "v1beta3.EventList", + "required": [ + "items" + ], + "properties": { + "apiVersion": { + "type": "string" + }, + "items": { + "type": "array", + "items": [ + { + "$ref": "v1beta3.Event" + } + ] + }, + "kind": { + "type": "string" + }, + "resourceVersion": { + "type": "string" + }, + "selfLink": { + "type": "string" + } + } + }, + "v1beta3.EventSource": { + "id": "v1beta3.EventSource", + "properties": { + "component": { + "type": "string" + }, + "host": { + "type": "string" + } + } + }, + "v1beta3.ExecAction": { + "id": "v1beta3.ExecAction", + "properties": { + "command": { + "type": "array", + "items": [ + { + "$ref": "string" + } + ] + } + } + }, + "v1beta3.GCEPersistentDisk": { + "id": "v1beta3.GCEPersistentDisk", + "required": [ + "pdName" + ], + "properties": { + "fsType": { + "type": "string" + }, + "partition": { + "type": "integer", + "format": "int32" + }, + "pdName": { + "type": "string" + }, + "readOnly": { + "type": "boolean" + } + } + }, + "v1beta3.GitRepo": { + "id": "v1beta3.GitRepo", + "required": [ + "repository", + "revision" + ], + "properties": { + "repository": { + "type": "string" + }, + "revision": { + "type": "string" + } + } + }, + "v1beta3.HTTPGetAction": { + "id": "v1beta3.HTTPGetAction", + "properties": { + "host": { + "type": "string" + }, + "path": { + "type": "string" + }, + "port": { + "type": "string" + } + } + }, + "v1beta3.Handler": { + "id": "v1beta3.Handler", + "properties": { + "exec": { + "type": "v1beta3.ExecAction" + }, + "httpGet": { + "type": "v1beta3.HTTPGetAction" + }, + "tcpSocket": { + "type": "v1beta3.TCPSocketAction" + } + } + }, + "v1beta3.HostPath": { + "id": "v1beta3.HostPath", + "required": [ + "path" + ], + "properties": { + "path": { + "type": "string" + } + } + }, + "v1beta3.Lifecycle": { + "id": "v1beta3.Lifecycle", + "properties": { + "postStart": { + "type": "v1beta3.Handler" + }, + "preStop": { + "type": "v1beta3.Handler" + } + } + }, + "v1beta3.LimitRange": { + "id": "v1beta3.LimitRange", + "properties": { + "annotations": { + "type": "v1beta3.ObjectMeta.annotations" + }, + "apiVersion": { + "type": "string" + }, + "creationTimestamp": { + "type": "string" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "kind": { + "type": "string" + }, + "labels": { + "type": "v1beta3.ObjectMeta.labels" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "resourceVersion": { + "type": "string" + }, + "selfLink": { + "type": "string" + }, + "spec": { + "type": "v1beta3.LimitRangeSpec" + }, + "uid": { + "type": "types.UID" + } + } + }, + "v1beta3.LimitRangeItem": { + "id": "v1beta3.LimitRangeItem", + "properties": { + "max": { + "type": "v1beta3.ResourceList" + }, + "min": { + "type": "v1beta3.ResourceList" + }, + "type": { + "type": "v1beta3.LimitType" + } + } + }, + "v1beta3.LimitRangeList": { + "id": "v1beta3.LimitRangeList", + "required": [ + "items" + ], + "properties": { + "apiVersion": { + "type": "string" + }, + "items": { + "type": "array", + "items": [ + { + "$ref": "v1beta3.LimitRange" + } + ] + }, + "kind": { + "type": "string" + }, + "resourceVersion": { + "type": "string" + }, + "selfLink": { + "type": "string" + } + } + }, + "v1beta3.LimitRangeSpec": { + "id": "v1beta3.LimitRangeSpec", + "required": [ + "limits" + ], + "properties": { + "limits": { + "type": "array", + "items": [ + { + "$ref": "v1beta3.LimitRangeItem" + } + ] + } + } + }, + "v1beta3.Node": { + "id": "v1beta3.Node", + "properties": { + "annotations": { + "type": "v1beta3.ObjectMeta.annotations" + }, + "apiVersion": { + "type": "string" + }, + "creationTimestamp": { + "type": "string" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "kind": { + "type": "string" + }, + "labels": { + "type": "v1beta3.ObjectMeta.labels" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "resourceVersion": { + "type": "string" + }, + "selfLink": { + "type": "string" + }, + "spec": { + "type": "v1beta3.NodeSpec" + }, + "status": { + "type": "v1beta3.NodeStatus" + }, + "uid": { + "type": "types.UID" + } + } + }, + "v1beta3.NodeCondition": { + "id": "v1beta3.NodeCondition", + "required": [ + "kind", + "status" + ], + "properties": { + "kind": { + "type": "v1beta3.NodeConditionKind" + }, + "lastTransitionTime": { + "type": "string" + }, + "message": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "status": { + "type": "v1beta3.NodeConditionStatus" + } + } + }, + "v1beta3.NodeList": { + "id": "v1beta3.NodeList", + "required": [ + "items" + ], + "properties": { + "apiVersion": { + "type": "string" + }, + "items": { + "type": "array", + "items": [ + { + "$ref": "v1beta3.Node" + } + ] + }, + "kind": { + "type": "string" + }, + "resourceVersion": { + "type": "string" + }, + "selfLink": { + "type": "string" + } + } + }, + "v1beta3.NodeSpec": { + "id": "v1beta3.NodeSpec", + "properties": { + "capacity": { + "type": "v1beta3.ResourceList" + } + } + }, + "v1beta3.NodeStatus": { + "id": "v1beta3.NodeStatus", + "properties": { + "conditions": { + "type": "array", + "items": [ + { + "$ref": "v1beta3.NodeCondition" + } + ] + }, + "hostIP": { + "type": "string" + }, + "phase": { + "type": "v1beta3.NodePhase" + } + } + }, + "v1beta3.ObjectReference": { + "id": "v1beta3.ObjectReference", + "properties": { + "apiVersion": { + "type": "string" + }, + "fieldPath": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "resourceVersion": { + "type": "string" + }, + "uid": { + "type": "types.UID" + } + } + }, + "v1beta3.Pod": { + "id": "v1beta3.Pod", + "properties": { + "annotations": { + "type": "v1beta3.ObjectMeta.annotations" + }, + "apiVersion": { + "type": "string" + }, + "creationTimestamp": { + "type": "string" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "kind": { + "type": "string" + }, + "labels": { + "type": "v1beta3.ObjectMeta.labels" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "resourceVersion": { + "type": "string" + }, + "selfLink": { + "type": "string" + }, + "spec": { + "type": "v1beta3.PodSpec" + }, + "status": { + "type": "v1beta3.PodStatus" + }, + "uid": { + "type": "types.UID" + } + } + }, + "v1beta3.PodList": { + "id": "v1beta3.PodList", + "required": [ + "items" + ], + "properties": { + "apiVersion": { + "type": "string" + }, + "items": { + "type": "array", + "items": [ + { + "$ref": "v1beta3.Pod" + } + ] + }, + "kind": { + "type": "string" + }, + "resourceVersion": { + "type": "string" + }, + "selfLink": { + "type": "string" + } + } + }, + "v1beta3.PodSpec": { + "id": "v1beta3.PodSpec", + "required": [ + "volumes", + "containers" + ], + "properties": { + "containers": { + "type": "array", + "items": [ + { + "$ref": "v1beta3.Container" + } + ] + }, + "dnsPolicy": { + "type": "v1beta3.DNSPolicy" + }, + "host": { + "type": "string", + "description": "host requested for this pod" + }, + "nodeSelector": { + "type": "v1beta3.PodSpec.nodeSelector" + }, + "restartPolicy": { + "type": "v1beta3.RestartPolicy" + }, + "volumes": { + "type": "array", + "items": [ + { + "$ref": "v1beta3.Volume" + } + ] + } + } + }, + "v1beta3.PodSpec.nodeSelector": { + "id": "v1beta3.PodSpec.nodeSelector", + "properties": {} + }, + "v1beta3.PodStatus": { + "id": "v1beta3.PodStatus", + "properties": { + "host": { + "type": "string" + }, + "hostIP": { + "type": "string" + }, + "info": { + "type": "v1beta3.PodInfo" + }, + "message": { + "type": "string" + }, + "phase": { + "type": "v1beta3.PodPhase" + }, + "podIP": { + "type": "string" + } + } + }, + "v1beta3.PodTemplateSpec": { + "id": "v1beta3.PodTemplateSpec", + "properties": { + "annotations": { + "type": "v1beta3.ObjectMeta.annotations" + }, + "creationTimestamp": { + "type": "string" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "labels": { + "type": "v1beta3.ObjectMeta.labels" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "resourceVersion": { + "type": "string" + }, + "selfLink": { + "type": "string" + }, + "spec": { + "type": "v1beta3.PodSpec" + }, + "uid": { + "type": "types.UID" + } + } + }, + "v1beta3.Port": { + "id": "v1beta3.Port", + "required": [ + "containerPort" + ], + "properties": { + "containerPort": { + "type": "integer", + "format": "int32" + }, + "hostIP": { + "type": "string" + }, + "hostPort": { + "type": "integer", + "format": "int32" + }, + "name": { + "type": "string" + }, + "protocol": { + "type": "v1beta3.Protocol" + } + } + }, + "v1beta3.Probe": { + "id": "v1beta3.Probe", + "properties": { + "exec": { + "type": "v1beta3.ExecAction" + }, + "httpGet": { + "type": "v1beta3.HTTPGetAction" + }, + "initialDelaySeconds": { + "type": "integer", + "format": "int64" + }, + "tcpSocket": { + "type": "v1beta3.TCPSocketAction" + } + } + }, + "v1beta3.ReplicationController": { + "id": "v1beta3.ReplicationController", + "properties": { + "annotations": { + "type": "v1beta3.ObjectMeta.annotations" + }, + "apiVersion": { + "type": "string" + }, + "creationTimestamp": { + "type": "string" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "kind": { + "type": "string" + }, + "labels": { + "type": "v1beta3.ObjectMeta.labels" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "resourceVersion": { + "type": "string" + }, + "selfLink": { + "type": "string" + }, + "spec": { + "type": "v1beta3.ReplicationControllerSpec" + }, + "status": { + "type": "v1beta3.ReplicationControllerStatus" + }, + "uid": { + "type": "types.UID" + } + } + }, + "v1beta3.ReplicationControllerList": { + "id": "v1beta3.ReplicationControllerList", + "required": [ + "items" + ], + "properties": { + "apiVersion": { + "type": "string" + }, + "items": { + "type": "array", + "items": [ + { + "$ref": "v1beta3.ReplicationController" + } + ] + }, + "kind": { + "type": "string" + }, + "resourceVersion": { + "type": "string" + }, + "selfLink": { + "type": "string" + } + } + }, + "v1beta3.ReplicationControllerSpec": { + "id": "v1beta3.ReplicationControllerSpec", + "required": [ + "replicas" + ], + "properties": { + "replicas": { + "type": "integer", + "format": "int32" + }, + "selector": { + "type": "v1beta3.ReplicationControllerSpec.selector" + }, + "template": { + "type": "v1beta3.PodTemplateSpec" + }, + "templateRef": { + "type": "v1beta3.ObjectReference" + } + } + }, + "v1beta3.ReplicationControllerSpec.selector": { + "id": "v1beta3.ReplicationControllerSpec.selector", + "properties": {} + }, + "v1beta3.ReplicationControllerStatus": { + "id": "v1beta3.ReplicationControllerStatus", + "required": [ + "replicas" + ], + "properties": { + "replicas": { + "type": "integer", + "format": "int32" + } + } + }, + "v1beta3.ResourceQuota": { + "id": "v1beta3.ResourceQuota", + "properties": { + "annotations": { + "type": "v1beta3.ObjectMeta.annotations" + }, + "apiVersion": { + "type": "string" + }, + "creationTimestamp": { + "type": "string" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "kind": { + "type": "string" + }, + "labels": { + "type": "v1beta3.ObjectMeta.labels" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "resourceVersion": { + "type": "string" + }, + "selfLink": { + "type": "string" + }, + "spec": { + "type": "v1beta3.ResourceQuotaSpec" + }, + "status": { + "type": "v1beta3.ResourceQuotaStatus" + }, + "uid": { + "type": "types.UID" + } + } + }, + "v1beta3.ResourceQuotaList": { + "id": "v1beta3.ResourceQuotaList", + "required": [ + "items" + ], + "properties": { + "apiVersion": { + "type": "string" + }, + "items": { + "type": "array", + "items": [ + { + "$ref": "v1beta3.ResourceQuota" + } + ] + }, + "kind": { + "type": "string" + }, + "resourceVersion": { + "type": "string" + }, + "selfLink": { + "type": "string" + } + } + }, + "v1beta3.ResourceQuotaSpec": { + "id": "v1beta3.ResourceQuotaSpec", + "properties": { + "hard": { + "type": "v1beta3.ResourceList" + } + } + }, + "v1beta3.ResourceQuotaStatus": { + "id": "v1beta3.ResourceQuotaStatus", + "properties": { + "hard": { + "type": "v1beta3.ResourceList" + }, + "used": { + "type": "v1beta3.ResourceList" + } + } + }, + "v1beta3.ResourceQuotaUsage": { + "id": "v1beta3.ResourceQuotaUsage", + "properties": { + "annotations": { + "type": "v1beta3.ObjectMeta.annotations" + }, + "apiVersion": { + "type": "string" + }, + "creationTimestamp": { + "type": "string" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "kind": { + "type": "string" + }, + "labels": { + "type": "v1beta3.ObjectMeta.labels" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "resourceVersion": { + "type": "string" + }, + "selfLink": { + "type": "string" + }, + "status": { + "type": "v1beta3.ResourceQuotaStatus" + }, + "uid": { + "type": "types.UID" + } + } + }, + "v1beta3.ResourceRequirementSpec": { + "id": "v1beta3.ResourceRequirementSpec", + "properties": { + "limits": { + "type": "v1beta3.ResourceList", + "description": "Maximum amount of compute resources allowed" + } + } + }, + "v1beta3.RestartPolicy": { + "id": "v1beta3.RestartPolicy", + "properties": { + "always": { + "type": "v1beta3.RestartPolicyAlways" + }, + "never": { + "type": "v1beta3.RestartPolicyNever" + }, + "onFailure": { + "type": "v1beta3.RestartPolicyOnFailure" + } + } + }, + "v1beta3.RestartPolicyAlways": { + "id": "v1beta3.RestartPolicyAlways", + "properties": {} + }, + "v1beta3.RestartPolicyNever": { + "id": "v1beta3.RestartPolicyNever", + "properties": {} + }, + "v1beta3.RestartPolicyOnFailure": { + "id": "v1beta3.RestartPolicyOnFailure", + "properties": {} + }, + "v1beta3.Service": { + "id": "v1beta3.Service", + "properties": { + "annotations": { + "type": "v1beta3.ObjectMeta.annotations" + }, + "apiVersion": { + "type": "string" + }, + "creationTimestamp": { + "type": "string" + }, + "generateName": { + "type": "string", + "description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified" + }, + "kind": { + "type": "string" + }, + "labels": { + "type": "v1beta3.ObjectMeta.labels" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "resourceVersion": { + "type": "string" + }, + "selfLink": { + "type": "string" + }, + "spec": { + "type": "v1beta3.ServiceSpec" + }, + "status": { + "type": "v1beta3.ServiceStatus" + }, + "uid": { + "type": "types.UID" + } + } + }, + "v1beta3.ServiceSpec": { + "id": "v1beta3.ServiceSpec", + "required": [ + "port", + "selector" + ], + "properties": { + "containerPort": { + "type": "string" + }, + "createExternalLoadBalancer": { + "type": "boolean" + }, + "port": { + "type": "integer", + "format": "int32" + }, + "portalIP": { + "type": "string" + }, + "protocol": { + "type": "v1beta3.Protocol" + }, + "publicIPs": { + "type": "array", + "items": [ + { + "$ref": "string" + } + ] + }, + "selector": { + "type": "v1beta3.ServiceSpec.selector" + }, + "sessionAffinity": { + "type": "v1beta3.AffinityType" + } + } + }, + "v1beta3.ServiceSpec.selector": { + "id": "v1beta3.ServiceSpec.selector", + "properties": {} + }, + "v1beta3.ServiceStatus": { + "id": "v1beta3.ServiceStatus", + "properties": {} + }, + "v1beta3.TCPSocketAction": { + "id": "v1beta3.TCPSocketAction", + "properties": { + "port": { + "type": "string" + } + } + }, + "v1beta3.Volume": { + "id": "v1beta3.Volume", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + }, + "source": { + "type": "v1beta3.VolumeSource" + } + } + }, + "v1beta3.VolumeMount": { + "id": "v1beta3.VolumeMount", + "required": [ + "name", + "mountPath" + ], + "properties": { + "mountPath": { + "type": "string" + }, + "name": { + "type": "string" + }, + "readOnly": { + "type": "boolean" + } + } + }, + "v1beta3.VolumeSource": { + "id": "v1beta3.VolumeSource", + "required": [ + "hostPath", + "emptyDir", + "gcePersistentDisk", + "gitRepo" + ], + "properties": { + "emptyDir": { + "type": "v1beta3.EmptyDir" + }, + "gcePersistentDisk": { + "type": "v1beta3.GCEPersistentDisk" + }, + "gitRepo": { + "type": "v1beta3.GitRepo" + }, + "hostPath": { + "type": "v1beta3.HostPath" + } + } + } + } + } \ No newline at end of file diff --git a/api/swagger-spec/version.json b/api/swagger-spec/version.json new file mode 100644 index 00000000000..a6f262cfe8a --- /dev/null +++ b/api/swagger-spec/version.json @@ -0,0 +1,27 @@ +{ + "swaggerVersion": "1.2", + "apiVersion": "", + "basePath": "127.0.0.1:8050", + "resourcePath": "/version", + "apis": [ + { + "path": "/version", + "description": "git code version from which this is built", + "operations": [ + { + "type": "void", + "method": "GET", + "summary": "get the code version", + "nickname": "getCodeVersion", + "parameters": [], + "produces": [ + "application/json" + ], + "consumes": [ + "application/json" + ] + } + ] + } + ] + } \ No newline at end of file diff --git a/hack/update-swagger-spec.sh b/hack/update-swagger-spec.sh new file mode 100755 index 00000000000..8ff143d205a --- /dev/null +++ b/hack/update-swagger-spec.sh @@ -0,0 +1,71 @@ +#!/bin/bash + +# Copyright 2015 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Script to fetch latest swagger spec. +# Puts the updated spec at swagger-spec/ + +set -o errexit +set -o nounset +set -o pipefail + +KUBE_ROOT=$(dirname "${BASH_SOURCE}")/.. +SWAGGER_ROOT_DIR="${KUBE_ROOT}/api/swagger-spec" +source "${KUBE_ROOT}/hack/lib/init.sh" + +function cleanup() +{ + [[ -n ${APISERVER_PID-} ]] && kill ${APISERVER_PID} 1>&2 2>/dev/null + + kube::etcd::cleanup + + kube::log::status "Clean up complete" +} + +trap cleanup EXIT SIGINT + +kube::etcd::start + +ETCD_HOST=${ETCD_HOST:-127.0.0.1} +ETCD_PORT=${ETCD_PORT:-4001} +API_PORT=${API_PORT:-8050} +API_HOST=${API_HOST:-127.0.0.1} +KUBELET_PORT=${KUBELET_PORT:-10250} + +# Start kube-apiserver +kube::log::status "Starting kube-apiserver" +"${KUBE_OUTPUT_HOSTBIN}/kube-apiserver" \ + --address="127.0.0.1" \ + --public_address_override="127.0.0.1" \ + --port="${API_PORT}" \ + --etcd_servers="http://${ETCD_HOST}:${ETCD_PORT}" \ + --public_address_override="127.0.0.1" \ + --kubelet_port=${KUBELET_PORT} \ + --runtime_config=api/v1beta3 \ + --portal_net="10.0.0.0/24" 1>&2 & +APISERVER_PID=$! + +kube::util::wait_for_url "http://127.0.0.1:${API_PORT}/healthz" "apiserver: " + +SWAGGER_API_PATH="http://127.0.0.1:${API_PORT}/swaggerapi/" +kube::log::status "Updating " ${SWAGGER_ROOT_DIR} +curl ${SWAGGER_API_PATH} > ${SWAGGER_ROOT_DIR}/resourceListing.json +curl ${SWAGGER_API_PATH}version > ${SWAGGER_ROOT_DIR}/version.json +curl ${SWAGGER_API_PATH}api > ${SWAGGER_ROOT_DIR}/api.json +curl ${SWAGGER_API_PATH}api/v1beta1 > ${SWAGGER_ROOT_DIR}/v1beta1.json +curl ${SWAGGER_API_PATH}api/v1beta2 > ${SWAGGER_ROOT_DIR}/v1beta2.json +curl ${SWAGGER_API_PATH}api/v1beta3 > ${SWAGGER_ROOT_DIR}/v1beta3.json + +kube::log::status "SUCCESS" From 9261c5a6555f09822678ba69a66f0078b17a5488 Mon Sep 17 00:00:00 2001 From: Zach Loafman Date: Thu, 5 Feb 2015 14:35:24 -0800 Subject: [PATCH 48/50] Remove timeout from cmd/e2e now that all tests are under it --- test/e2e/driver.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/test/e2e/driver.go b/test/e2e/driver.go index 7f2664f2ebb..95c4b84e0f8 100644 --- a/test/e2e/driver.go +++ b/test/e2e/driver.go @@ -20,7 +20,6 @@ import ( "path" "regexp" "strings" - "time" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/golang/glog" @@ -48,17 +47,6 @@ func RunE2ETests(authConfig, certDir, host, repoRoot, provider string, orderseed util.InitLogs() defer util.FlushLogs() - // TODO: Associate a timeout with each test individually. - go func() { - defer util.FlushLogs() - // TODO: We should modify testSpec to include an estimated running time - // for each test and use that information to estimate a timeout - // value. Until then, as we add more tests (and before we move to - // parallel testing) we need to adjust this value as we add more tests. - time.Sleep(40 * time.Minute) - glog.Fatalf("This test has timed out. Cleanup not guaranteed.") - }() - if len(testList) != 0 { if config.GinkgoConfig.FocusString != "" || config.GinkgoConfig.SkipString != "" { glog.Fatal("Either specify --test/-t or --ginkgo.focus/--ginkgo.skip but not both.") From df26b43a9dece0a32a6e37b199012a95c86c41be Mon Sep 17 00:00:00 2001 From: rsokolowski Date: Thu, 5 Feb 2015 23:56:26 +0100 Subject: [PATCH 49/50] Fix hack/e2e-test.sh broken due to removed flag from hack/e2e.go --- hack/e2e-test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hack/e2e-test.sh b/hack/e2e-test.sh index 6c8e7809ba0..284d270609b 100755 --- a/hack/e2e-test.sh +++ b/hack/e2e-test.sh @@ -15,6 +15,6 @@ # limitations under the License. # Provided for backwards compatibility -go run "$(dirname $0)/e2e.go" -v -build -up -tests="*" -down +go run "$(dirname $0)/e2e.go" -v -build -up -test -down exit $? From 14bfec92f2bffa1af1bbe29362a384364832cc5f Mon Sep 17 00:00:00 2001 From: Mike Danese Date: Fri, 30 Jan 2015 17:03:57 -0800 Subject: [PATCH 50/50] rename probe.Status to probe.Result. --- cmd/integration/integration.go | 2 +- pkg/apiserver/validator.go | 4 ++-- pkg/apiserver/validator_test.go | 2 +- pkg/client/kubelet.go | 6 +++--- pkg/cloudprovider/controller/nodecontroller_test.go | 4 ++-- pkg/kubelet/kubelet.go | 2 +- pkg/kubelet/probe.go | 3 +-- pkg/probe/exec/exec.go | 2 +- pkg/probe/exec/exec_test.go | 2 +- pkg/probe/http/http.go | 4 ++-- pkg/probe/http/http_test.go | 2 +- pkg/probe/probe.go | 6 +++--- pkg/probe/tcp/tcp.go | 4 ++-- pkg/probe/tcp/tcp_test.go | 2 +- 14 files changed, 22 insertions(+), 23 deletions(-) diff --git a/cmd/integration/integration.go b/cmd/integration/integration.go index 7a7ab6bf860..b4c47a3fcb8 100644 --- a/cmd/integration/integration.go +++ b/cmd/integration/integration.go @@ -96,7 +96,7 @@ func (fakeKubeletClient) GetPodStatus(host, podNamespace, podID string) (api.Pod return r, nil } -func (fakeKubeletClient) HealthCheck(host string) (probe.Status, error) { +func (fakeKubeletClient) HealthCheck(host string) (probe.Result, error) { return probe.Success, nil } diff --git a/pkg/apiserver/validator.go b/pkg/apiserver/validator.go index e1c47803cb9..d73a418c530 100644 --- a/pkg/apiserver/validator.go +++ b/pkg/apiserver/validator.go @@ -46,7 +46,7 @@ type validator struct { } // TODO: can this use pkg/probe/http -func (s *Server) check(client httpGet) (probe.Status, string, error) { +func (s *Server) check(client httpGet) (probe.Result, string, error) { resp, err := client.Get("http://" + net.JoinHostPort(s.Addr, strconv.Itoa(s.Port)) + s.Path) if err != nil { return probe.Unknown, "", err @@ -66,7 +66,7 @@ func (s *Server) check(client httpGet) (probe.Status, string, error) { type ServerStatus struct { Component string `json:"component,omitempty"` Health string `json:"health,omitempty"` - HealthCode probe.Status `json:"healthCode,omitempty"` + HealthCode probe.Result `json:"healthCode,omitempty"` Msg string `json:"msg,omitempty"` Err string `json:"err,omitempty"` } diff --git a/pkg/apiserver/validator_test.go b/pkg/apiserver/validator_test.go index 0a9477135a2..35b991aee0e 100644 --- a/pkg/apiserver/validator_test.go +++ b/pkg/apiserver/validator_test.go @@ -54,7 +54,7 @@ func TestValidate(t *testing.T) { tests := []struct { err error data string - expectedStatus probe.Status + expectedStatus probe.Result code int expectErr bool }{ diff --git a/pkg/client/kubelet.go b/pkg/client/kubelet.go index f5754bb91b9..72882b5af7a 100644 --- a/pkg/client/kubelet.go +++ b/pkg/client/kubelet.go @@ -41,7 +41,7 @@ type KubeletClient interface { // KubeletHealthchecker is an interface for healthchecking kubelets type KubeletHealthChecker interface { - HealthCheck(host string) (probe.Status, error) + HealthCheck(host string) (probe.Result, error) } // PodInfoGetter is an interface for things that can get information about a pod's containers. @@ -134,7 +134,7 @@ func (c *HTTPKubeletClient) GetPodStatus(host, podNamespace, podID string) (api. return status, nil } -func (c *HTTPKubeletClient) HealthCheck(host string) (probe.Status, error) { +func (c *HTTPKubeletClient) HealthCheck(host string) (probe.Result, error) { return httprobe.DoHTTPProbe(fmt.Sprintf("%s/healthz", c.url(host)), c.Client) } @@ -148,6 +148,6 @@ func (c FakeKubeletClient) GetPodStatus(host, podNamespace string, podID string) return api.PodStatusResult{}, errors.New("Not Implemented") } -func (c FakeKubeletClient) HealthCheck(host string) (probe.Status, error) { +func (c FakeKubeletClient) HealthCheck(host string) (probe.Result, error) { return probe.Unknown, errors.New("Not Implemented") } diff --git a/pkg/cloudprovider/controller/nodecontroller_test.go b/pkg/cloudprovider/controller/nodecontroller_test.go index 4462a74fbcc..69f53cc0832 100644 --- a/pkg/cloudprovider/controller/nodecontroller_test.go +++ b/pkg/cloudprovider/controller/nodecontroller_test.go @@ -102,7 +102,7 @@ func (m *FakeNodeHandler) Update(node *api.Node) (*api.Node, error) { // FakeKubeletClient is a fake implementation of KubeletClient. type FakeKubeletClient struct { - Status probe.Status + Status probe.Result Err error } @@ -110,7 +110,7 @@ func (c *FakeKubeletClient) GetPodStatus(host, podNamespace, podID string) (api. return api.PodStatusResult{}, errors.New("Not Implemented") } -func (c *FakeKubeletClient) HealthCheck(host string) (probe.Status, error) { +func (c *FakeKubeletClient) HealthCheck(host string) (probe.Result, error) { return c.Status, c.Err } diff --git a/pkg/kubelet/kubelet.go b/pkg/kubelet/kubelet.go index b5454f933ec..815e15395a9 100644 --- a/pkg/kubelet/kubelet.go +++ b/pkg/kubelet/kubelet.go @@ -1446,7 +1446,7 @@ func (kl *Kubelet) GetPodStatus(podFullName string, uid types.UID) (api.PodStatu return podStatus, err } -func (kl *Kubelet) probeLiveness(podFullName string, podUID types.UID, status api.PodStatus, container api.Container, dockerContainer *docker.APIContainers) (healthStatus probe.Status, err error) { +func (kl *Kubelet) probeLiveness(podFullName string, podUID types.UID, status api.PodStatus, container api.Container, dockerContainer *docker.APIContainers) (healthStatus probe.Result, err error) { // Give the container 60 seconds to start up. if container.LivenessProbe == nil { return probe.Success, nil diff --git a/pkg/kubelet/probe.go b/pkg/kubelet/probe.go index 72bc972c5f4..a3dc573ad85 100644 --- a/pkg/kubelet/probe.go +++ b/pkg/kubelet/probe.go @@ -39,7 +39,7 @@ var ( tcprober = tcprobe.New() ) -func (kl *Kubelet) probeContainer(p *api.Probe, podFullName string, podUID types.UID, status api.PodStatus, container api.Container) (probe.Status, error) { +func (kl *Kubelet) probeContainer(p *api.Probe, podFullName string, podUID types.UID, status api.PodStatus, container api.Container) (probe.Result, error) { var timeout time.Duration secs := container.LivenessProbe.TimeoutSeconds if secs > 0 { @@ -47,7 +47,6 @@ func (kl *Kubelet) probeContainer(p *api.Probe, podFullName string, podUID types } else { timeout = 1 * time.Second } - if p.Exec != nil { return execprober.Probe(kl.newExecInContainer(podFullName, podUID, container)) } diff --git a/pkg/probe/exec/exec.go b/pkg/probe/exec/exec.go index ac5a0f2ccc6..f44c1f111b5 100644 --- a/pkg/probe/exec/exec.go +++ b/pkg/probe/exec/exec.go @@ -33,7 +33,7 @@ func New() ExecProber { type ExecProber struct{} -func (pr ExecProber) Probe(e uexec.Cmd) (probe.Status, error) { +func (pr ExecProber) Probe(e uexec.Cmd) (probe.Result, error) { data, err := e.CombinedOutput() glog.V(4).Infof("health check response: %s", string(data)) if err != nil { diff --git a/pkg/probe/exec/exec_test.go b/pkg/probe/exec/exec_test.go index a2f21365abd..22e7e9b1f54 100644 --- a/pkg/probe/exec/exec_test.go +++ b/pkg/probe/exec/exec_test.go @@ -35,7 +35,7 @@ func (f *FakeCmd) CombinedOutput() ([]byte, error) { func (f *FakeCmd) SetDir(dir string) {} type healthCheckTest struct { - expectedStatus probe.Status + expectedStatus probe.Result expectError bool output []byte err error diff --git a/pkg/probe/http/http.go b/pkg/probe/http/http.go index 09f89178823..65252973198 100644 --- a/pkg/probe/http/http.go +++ b/pkg/probe/http/http.go @@ -38,7 +38,7 @@ type HTTPProber struct { } // Probe returns a ProbeRunner capable of running an http check. -func (pr *HTTPProber) Probe(host string, port int, path string, timeout time.Duration) (probe.Status, error) { +func (pr *HTTPProber) Probe(host string, port int, path string, timeout time.Duration) (probe.Result, error) { return DoHTTPProbe(formatURL(host, port, path), &http.Client{Timeout: timeout, Transport: pr.transport}) } @@ -50,7 +50,7 @@ type HTTPGetInterface interface { // If the HTTP response code is successful (i.e. 400 > code >= 200), it returns Success. // If the HTTP response code is unsuccessful or HTTP communication fails, it returns Failure. // This is exported because some other packages may want to do direct HTTP probes. -func DoHTTPProbe(url string, client HTTPGetInterface) (probe.Status, error) { +func DoHTTPProbe(url string, client HTTPGetInterface) (probe.Result, error) { res, err := client.Get(url) if err != nil { glog.V(1).Infof("HTTP probe error: %v", err) diff --git a/pkg/probe/http/http_test.go b/pkg/probe/http/http_test.go index be9b0ae8ce4..7d1392d4f19 100644 --- a/pkg/probe/http/http_test.go +++ b/pkg/probe/http/http_test.go @@ -54,7 +54,7 @@ func TestHTTPProbeChecker(t *testing.T) { prober := New() testCases := []struct { handler func(w http.ResponseWriter) - health probe.Status + health probe.Result }{ // The probe will be filled in below. This is primarily testing that an HTTP GET happens. {handleReq(http.StatusOK), probe.Success}, diff --git a/pkg/probe/probe.go b/pkg/probe/probe.go index b9d1a363a38..72111d4eea2 100644 --- a/pkg/probe/probe.go +++ b/pkg/probe/probe.go @@ -16,16 +16,16 @@ limitations under the License. package probe -type Status int +type Result int // Status values must be one of these constants. const ( - Success Status = iota + Success Result = iota Failure Unknown ) -func (s Status) String() string { +func (s Result) String() string { switch s { case Success: return "success" diff --git a/pkg/probe/tcp/tcp.go b/pkg/probe/tcp/tcp.go index 8232295a0b2..b1925d26e3f 100644 --- a/pkg/probe/tcp/tcp.go +++ b/pkg/probe/tcp/tcp.go @@ -32,7 +32,7 @@ func New() TCPProber { type TCPProber struct{} -func (pr TCPProber) Probe(host string, port int, timeout time.Duration) (probe.Status, error) { +func (pr TCPProber) Probe(host string, port int, timeout time.Duration) (probe.Result, error) { return DoTCPProbe(net.JoinHostPort(host, strconv.Itoa(port)), timeout) } @@ -40,7 +40,7 @@ func (pr TCPProber) Probe(host string, port int, timeout time.Duration) (probe.S // If the socket can be opened, it returns Success // If the socket fails to open, it returns Failure. // This is exported because some other packages may want to do direct TCP probes. -func DoTCPProbe(addr string, timeout time.Duration) (probe.Status, error) { +func DoTCPProbe(addr string, timeout time.Duration) (probe.Result, error) { conn, err := net.DialTimeout("tcp", addr, timeout) if err != nil { return probe.Failure, nil diff --git a/pkg/probe/tcp/tcp_test.go b/pkg/probe/tcp/tcp_test.go index 5be9125eb74..47b93eb796a 100644 --- a/pkg/probe/tcp/tcp_test.go +++ b/pkg/probe/tcp/tcp_test.go @@ -31,7 +31,7 @@ import ( func TestTcpHealthChecker(t *testing.T) { prober := New() tests := []struct { - expectedStatus probe.Status + expectedStatus probe.Result usePort bool expectError bool }{