diff --git a/client/client.go b/client/client.go index ae56304586f6ffa0ca372b9938ef53357c9985e2..f7739d36b91b15b979b5b4444e23b946027e3e2c 100644 --- a/client/client.go +++ b/client/client.go @@ -16,9 +16,15 @@ package client import ( + "encoding/json" "fmt" + "io/ioutil" + "net/http" + "net/url" "os" + "path" "reflect" + "time" "github.com/ksonnet/ksonnet/metadata" str "github.com/ksonnet/ksonnet/strings" @@ -61,6 +67,59 @@ func InitClient(env string) (dynamic.ClientPool, discovery.DiscoveryInterface, s return clientConfig.RestClient(&env) } +// GetAPISpec reads the kubernetes API version from this client's swagger.json. +// We anticipate the swagger.json to be located at <server>/swagger.json. +// If no swagger is found, or we are unable to authenticate to the server, we +// will default to version:v1.7.0. +func (c *Config) GetAPISpec(server string) string { + const ( + defaultVersion = "version:v1.7.0" + ) + + type Info struct { + Version string `json:"version"` + } + + type Spec struct { + Info Info `json:"info"` + } + + u, err := url.Parse(server) + u.Path = path.Join(u.Path, "swagger.json") + url := u.String() + + client := http.Client{ + Timeout: time.Second * 2, + } + + req, err := http.NewRequest(http.MethodGet, url, nil) + if err != nil { + log.Debugf("Failed to create request at %s\n%s", url, err.Error()) + return defaultVersion + } + + res, err := client.Do(req) + if err != nil { + log.Debugf("Failed to open swagger at %s\n%s", url, err.Error()) + return defaultVersion + } + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + log.Debugf("Failed to read swagger at %s\n%s", url, err.Error()) + return defaultVersion + } + + spec := Spec{} + jsonErr := json.Unmarshal(body, &spec) + if jsonErr != nil { + log.Debugf("Failed to parse swagger at %s\n%s", url, err.Error()) + return defaultVersion + } + + return fmt.Sprintf("version:%s", spec.Info.Version) +} + // Namespace returns the namespace for the provided ClientConfig. func (c *Config) Namespace() (string, error) { ns, _, err := c.Config.Namespace() diff --git a/cmd/env.go b/cmd/env.go index 830a61803c2fdeb3b02463eed06ae2d7f3828195..8740c6176d249b936ab778000e3fd8ca5bbe58ff 100644 --- a/cmd/env.go +++ b/cmd/env.go @@ -140,6 +140,9 @@ var envAddCmd = &cobra.Command{ if err != nil { return err } + if specFlag == "" { + specFlag = envClientConfig.GetAPISpec(server) + } c, err := kubecfg.NewEnvAddCmd(name, server, namespace, specFlag, manager) if err != nil { diff --git a/cmd/init.go b/cmd/init.go index 52bd3dfd9f3ec4cbf344d36a30f37d9a59ba7194..dda460d2f58a574cc4bc45439e48807ac66b1318 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -23,7 +23,6 @@ import ( "github.com/ksonnet/ksonnet/client" "github.com/ksonnet/ksonnet/pkg/kubecfg" - log "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -39,7 +38,7 @@ var ( func init() { RootCmd.AddCommand(initCmd) // TODO: We need to make this default to checking the `kubeconfig` file. - initCmd.PersistentFlags().String(flagAPISpec, "version:v1.7.0", + initCmd.PersistentFlags().String(flagAPISpec, "", "Manually specified Kubernetes API version. The corresponding OpenAPI spec is used to generate ksonnet's Kubernetes libraries") initClientConfig = client.NewDefaultClientConfig() @@ -72,17 +71,19 @@ var initCmd = &cobra.Command{ return err } - specFlag, err := flags.GetString(flagAPISpec) + server, namespace, err := resolveEnvFlags(flags) if err != nil { return err } - server, namespace, err := resolveEnvFlags(flags) + specFlag, err := flags.GetString(flagAPISpec) if err != nil { return err } + if specFlag == "" { + specFlag = initClientConfig.GetAPISpec(server) + } - log.Infof("Creating a new app '%s' at path '%s'", appName, appRoot) c, err := kubecfg.NewInitCmd(appName, appRoot, &specFlag, &server, &namespace) if err != nil { return err diff --git a/docs/cli-reference/ks_init.md b/docs/cli-reference/ks_init.md index a1d3136567b69c1f03617b2ef525a612f1b6cb95..90efb78d3675de4464c83acfe2edd8857eeee574 100644 --- a/docs/cli-reference/ks_init.md +++ b/docs/cli-reference/ks_init.md @@ -81,7 +81,7 @@ ks init app-name --dir=custom-location ### Options ``` - --api-spec string Manually specified Kubernetes API version. The corresponding OpenAPI spec is used to generate ksonnet's Kubernetes libraries (default "version:v1.7.0") + --api-spec string Manually specified Kubernetes API version. The corresponding OpenAPI spec is used to generate ksonnet's Kubernetes libraries --as string Username to impersonate for the operation --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. --certificate-authority string Path to a cert file for the certificate authority