mirror of https://github.com/k3s-io/k3s
Add /version to server and check it in client.
Will help detect client/version skew and prevent e2e test from passing while running a version other than the one you think it's running.pull/6/head
parent
9fc52c8aaa
commit
3b8488028d
|
@ -22,6 +22,7 @@ import (
|
|||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -31,28 +32,28 @@ import (
|
|||
kube_client "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubecfg"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/version"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// AppVersion is the current version of kubecfg.
|
||||
const AppVersion = "0.1"
|
||||
|
||||
var (
|
||||
versionFlag = flag.Bool("V", false, "Print the version number.")
|
||||
httpServer = flag.String("h", "", "The host to connect to.")
|
||||
config = flag.String("c", "", "Path to the config file.")
|
||||
selector = flag.String("l", "", "Selector (label query) to use for listing")
|
||||
updatePeriod = flag.Duration("u", 60*time.Second, "Update interval period")
|
||||
portSpec = flag.String("p", "", "The port spec, comma-separated list of <external>:<internal>,...")
|
||||
servicePort = flag.Int("s", -1, "If positive, create and run a corresponding service on this port, only used with 'run'")
|
||||
authConfig = flag.String("auth", os.Getenv("HOME")+"/.kubernetes_auth", "Path to the auth info file. If missing, prompt the user. Only used if doing https.")
|
||||
json = flag.Bool("json", false, "If true, print raw JSON for responses")
|
||||
yaml = flag.Bool("yaml", false, "If true, print raw YAML for responses")
|
||||
verbose = flag.Bool("verbose", false, "If true, print extra information")
|
||||
proxy = flag.Bool("proxy", false, "If true, run a proxy to the api server")
|
||||
www = flag.String("www", "", "If -proxy is true, use this directory to serve static files")
|
||||
templateFile = flag.String("template_file", "", "If present, load this file as a golang template and use it for output printing")
|
||||
templateStr = flag.String("template", "", "If present, parse this string as a golang template and use it for output printing")
|
||||
versionFlag = flag.Bool("V", false, "Print the version number.")
|
||||
serverVersion = flag.Bool("server_version", false, "Print the server's version number.")
|
||||
preventSkew = flag.Bool("expect_version_match", false, "Fail if server's version doesn't match own version.")
|
||||
httpServer = flag.String("h", "", "The host to connect to.")
|
||||
config = flag.String("c", "", "Path to the config file.")
|
||||
selector = flag.String("l", "", "Selector (label query) to use for listing")
|
||||
updatePeriod = flag.Duration("u", 60*time.Second, "Update interval period")
|
||||
portSpec = flag.String("p", "", "The port spec, comma-separated list of <external>:<internal>,...")
|
||||
servicePort = flag.Int("s", -1, "If positive, create and run a corresponding service on this port, only used with 'run'")
|
||||
authConfig = flag.String("auth", os.Getenv("HOME")+"/.kubernetes_auth", "Path to the auth info file. If missing, prompt the user. Only used if doing https.")
|
||||
json = flag.Bool("json", false, "If true, print raw JSON for responses")
|
||||
yaml = flag.Bool("yaml", false, "If true, print raw YAML for responses")
|
||||
verbose = flag.Bool("verbose", false, "If true, print extra information")
|
||||
proxy = flag.Bool("proxy", false, "If true, run a proxy to the api server")
|
||||
www = flag.String("www", "", "If -proxy is true, use this directory to serve static files")
|
||||
templateFile = flag.String("template_file", "", "If present, load this file as a golang template and use it for output printing")
|
||||
templateStr = flag.String("template", "", "If present, parse this string as a golang template and use it for output printing")
|
||||
)
|
||||
|
||||
func usage() {
|
||||
|
@ -107,7 +108,7 @@ func main() {
|
|||
defer util.FlushLogs()
|
||||
|
||||
if *versionFlag {
|
||||
fmt.Println("Version:", AppVersion)
|
||||
fmt.Printf("Version: %#v\n", version.Get())
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
|
@ -136,6 +137,30 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
client := kube_client.New(masterServer, auth)
|
||||
|
||||
if *serverVersion {
|
||||
got, err := client.ServerVersion()
|
||||
if err != nil {
|
||||
fmt.Printf("Couldn't read version from server: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("Server Version: %#v\n", got)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if *preventSkew {
|
||||
got, err := client.ServerVersion()
|
||||
if err != nil {
|
||||
fmt.Printf("Couldn't read version from server: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if c, s := version.Get(), *got; !reflect.DeepEqual(c, s) {
|
||||
fmt.Printf("Server version (%#v) differs from client version (%#v)!\n", s, c)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if *proxy {
|
||||
glog.Info("Starting to serve on localhost:8001")
|
||||
server := kubecfg.NewProxyServer(*www, masterServer, auth)
|
||||
|
@ -148,8 +173,6 @@ func main() {
|
|||
}
|
||||
method := flag.Arg(0)
|
||||
|
||||
client := kube_client.New(masterServer, auth)
|
||||
|
||||
matchFound := executeAPIRequest(method, client) || executeControllerRequest(method, client)
|
||||
if matchFound == false {
|
||||
glog.Fatalf("Unknown command %s", method)
|
||||
|
|
|
@ -39,7 +39,7 @@ set -e
|
|||
# Use testing config
|
||||
export KUBE_CONFIG_FILE="config-test.sh"
|
||||
export KUBE_REPO_ROOT="$(dirname $0)/.."
|
||||
export CLOUDCFG="${KUBE_REPO_ROOT}/cluster/kubecfg.sh"
|
||||
export CLOUDCFG="${KUBE_REPO_ROOT}/cluster/kubecfg.sh -expect_version_match"
|
||||
|
||||
# Build a release required by the test provider [if any]
|
||||
test-build-release
|
||||
|
|
|
@ -106,7 +106,7 @@ APISERVER_PID=$!
|
|||
|
||||
wait_for_url "http://127.0.0.1:${API_PORT}/healthz" "apiserver: "
|
||||
|
||||
KUBE_CMD="${GO_OUT}/kubecfg -h http://127.0.0.1:${API_PORT}"
|
||||
KUBE_CMD="${GO_OUT}/kubecfg -h http://127.0.0.1:${API_PORT} -expect_version_match"
|
||||
|
||||
${KUBE_CMD} list pods
|
||||
echo "kubecfg(pods): ok"
|
||||
|
@ -130,4 +130,4 @@ echo "kubecfg(minions): ok"
|
|||
#PROXY_LOG=/tmp/kube-proxy.log
|
||||
#${GO_OUT}/proxy \
|
||||
# --etcd_servers="http://127.0.0.1:${ETCD_PORT}" 1>&2 &
|
||||
#PROXY_PID=$!
|
||||
#PROXY_PID=$!
|
||||
|
|
|
@ -39,6 +39,7 @@ import (
|
|||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/version"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
@ -151,6 +152,7 @@ func New(storage map[string]RESTStorage, prefix string) *APIServer {
|
|||
healthz.InstallHandler(s.mux)
|
||||
|
||||
s.mux.HandleFunc("/", s.handleIndex)
|
||||
s.mux.HandleFunc("/version", s.handleVersionReq)
|
||||
|
||||
// Handle both operations and operations/* with the same handler
|
||||
s.mux.HandleFunc(s.operationPrefix(), s.handleOperationRequest)
|
||||
|
@ -182,6 +184,11 @@ func (server *APIServer) handleIndex(w http.ResponseWriter, req *http.Request) {
|
|||
fmt.Fprint(w, data)
|
||||
}
|
||||
|
||||
// handleVersionReq writes the server's version information.
|
||||
func (server *APIServer) handleVersionReq(w http.ResponseWriter, req *http.Request) {
|
||||
server.writeRawJSON(http.StatusOK, version.Get(), w)
|
||||
}
|
||||
|
||||
func (server *APIServer) handleMinionReq(w http.ResponseWriter, req *http.Request) {
|
||||
minionPrefix := "/proxy/minion/"
|
||||
if !strings.HasPrefix(req.URL.Path, minionPrefix) {
|
||||
|
@ -344,14 +351,27 @@ func (server *APIServer) notFound(w http.ResponseWriter, req *http.Request) {
|
|||
fmt.Fprintf(w, "Not Found: %#v", req)
|
||||
}
|
||||
|
||||
// write writes an API object in wire format.
|
||||
func (server *APIServer) write(statusCode int, object interface{}, w http.ResponseWriter) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(statusCode)
|
||||
output, err := api.Encode(object)
|
||||
if err != nil {
|
||||
server.error(err, w)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(statusCode)
|
||||
w.Write(output)
|
||||
}
|
||||
|
||||
// writeRawJSON writes a non-API object in JSON.
|
||||
func (server *APIServer) writeRawJSON(statusCode int, object interface{}, w http.ResponseWriter) {
|
||||
output, err := json.Marshal(object)
|
||||
if err != nil {
|
||||
server.error(err, w)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(statusCode)
|
||||
w.Write(output)
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ package client
|
|||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
@ -26,6 +27,7 @@ import (
|
|||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/version"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
|
@ -238,3 +240,17 @@ func (c *Client) UpdateService(svc api.Service) (result api.Service, err error)
|
|||
func (c *Client) DeleteService(name string) error {
|
||||
return c.Delete().Path("services").Path(name).Do().Error()
|
||||
}
|
||||
|
||||
// ServerVersion retrieves and parses the server's version.
|
||||
func (c *Client) ServerVersion() (*version.Info, error) {
|
||||
body, err := c.Get().AbsPath("/version").Do().Raw()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var info version.Info
|
||||
err = json.Unmarshal(body, &info)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Got '%s': %v", string(body), err)
|
||||
}
|
||||
return &info, nil
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
|
@ -26,6 +27,7 @@ import (
|
|||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/version"
|
||||
)
|
||||
|
||||
// TODO: Move this to a common place, it's needed in multiple tests.
|
||||
|
@ -456,3 +458,30 @@ func TestDoRequestAcceptedSuccess(t *testing.T) {
|
|||
}
|
||||
fakeHandler.ValidateRequest(t, "/foo/bar", "GET", nil)
|
||||
}
|
||||
|
||||
func TestGetServerVersion(t *testing.T) {
|
||||
expect := version.Info{
|
||||
Major: "foo",
|
||||
Minor: "bar",
|
||||
GitCommit: "baz",
|
||||
}
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
output, err := json.Marshal(expect)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected encoding error: %v", err)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(output)
|
||||
}))
|
||||
client := New(server.URL, nil)
|
||||
|
||||
got, err := client.ServerVersion()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected encoding error: %v", err)
|
||||
}
|
||||
if e, a := expect, *got; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,10 +29,24 @@ import (
|
|||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/version"
|
||||
"github.com/golang/glog"
|
||||
"gopkg.in/v1/yaml"
|
||||
)
|
||||
|
||||
func GetServerVersion(client *client.Client) (*version.Info, error) {
|
||||
body, err := client.Get().AbsPath("/version").Do().Raw()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var info version.Info
|
||||
err = json.Unmarshal(body, &info)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Got '%s': %v", string(body), err)
|
||||
}
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
func promptForString(field string, r io.Reader) string {
|
||||
fmt.Printf("Please enter %s: ", field)
|
||||
var result string
|
||||
|
|
|
@ -317,9 +317,9 @@ func TestMakePorts(t *testing.T) {
|
|||
{
|
||||
"8080:80,8081:8081,443:444",
|
||||
[]api.Port{
|
||||
api.Port{HostPort: 8080, ContainerPort: 80},
|
||||
api.Port{HostPort: 8081, ContainerPort: 8081},
|
||||
api.Port{HostPort: 443, ContainerPort: 444},
|
||||
{HostPort: 8080, ContainerPort: 80},
|
||||
{HostPort: 8081, ContainerPort: 8081},
|
||||
{HostPort: 443, ContainerPort: 444},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -16,6 +16,21 @@ limitations under the License.
|
|||
|
||||
package version
|
||||
|
||||
func Get() (major, minor, gitCommit string) {
|
||||
return "v1beta", "1", commitFromGit
|
||||
// Info contains versioning information.
|
||||
// TODO: Add []string of api versions supported? It's still unclear
|
||||
// how we'll want to distribute that information.
|
||||
type Info struct {
|
||||
Major string `json:"major" yaml:"major"`
|
||||
Minor string `json:"minor" yaml:"minor"`
|
||||
GitCommit string `json:"gitCommit" yaml:"gitCommit"`
|
||||
}
|
||||
|
||||
// Get returns the overall codebase version. It's for detecting
|
||||
// what code a binary was built from.
|
||||
func Get() Info {
|
||||
return Info{
|
||||
Major: "0",
|
||||
Minor: "1",
|
||||
GitCommit: commitFromGit,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue