// SetKubernetesDefaults sets default values on the provided client config for accessing the // Kubernetes API or returns an error if any of the defaults are impossible or invalid. func SetKubernetesDefaults(config *Config) error { if config.Prefix == "" { config.Prefix = "/api" } if len(config.UserAgent) == 0 { config.UserAgent = DefaultKubernetesUserAgent() } if len(config.Version) == 0 { config.Version = defaultVersionFor(config) } version := config.Version versionInterfaces, err := latest.InterfacesFor(version) if err != nil { return fmt.Errorf("API version '%s' is not recognized (valid values: %s)", version, strings.Join(latest.Versions, ", ")) } if config.Codec == nil { config.Codec = versionInterfaces.Codec } if config.QPS == 0.0 { config.QPS = 5.0 } if config.Burst == 0 { config.Burst = 10 } return nil }
func findCodec(version string) (runtime.Codec, error) { versions, err := latest.InterfacesFor(version) if err != nil { return nil, err } return versions.Codec, nil }
// Converter returns the api.Scheme for the API version to test against, as set by the // KUBE_API_VERSION env var. func Converter() runtime.ObjectConvertor { interfaces, err := latest.InterfacesFor(Version()) if err != nil { panic(err) } return interfaces.ObjectConvertor }
// MetadataAccessor returns the MetadataAccessor for the API version to test against, // as set by the KUBE_API_VERSION env var. func MetadataAccessor() meta.MetadataAccessor { interfaces, err := latest.InterfacesFor(Version()) if err != nil { panic(err) } return interfaces.MetadataAccessor }
// Codec returns the codec for the API version to test against, as set by the // KUBE_API_VERSION env var. func Codec() runtime.Codec { interfaces, err := latest.InterfacesFor(Version()) if err != nil { panic(err) } return interfaces.Codec }
// forbidden renders a simple forbidden error func forbidden(reason, apiVersion string, w http.ResponseWriter, req *http.Request) { // the api version can be empty for two basic reasons: // 1. malformed API request // 2. not an API request at all // In these cases, just assume the latest version that will work better than nothing if len(apiVersion) == 0 { apiVersion = klatest.Version } // Reason is an opaque string that describes why access is allowed or forbidden (forbidden by the time we reach here). // We don't have direct access to kind or name (not that those apply either in the general case) // We create a NewForbidden to stay close the API, but then we override the message to get a serialization // that makes sense when a human reads it. forbiddenError, _ := kapierror.NewForbidden("", "", errors.New("")).(*kapierror.StatusError) forbiddenError.ErrStatus.Message = reason // Not all API versions in valid API requests will have a matching codec in kubernetes. If we can't find one, // just default to the latest kube codec. codec := klatest.Codec if requestedCodec, err := klatest.InterfacesFor(apiVersion); err == nil { codec = requestedCodec } formatted := &bytes.Buffer{} output, err := codec.Encode(&forbiddenError.ErrStatus) if err != nil { fmt.Fprintf(formatted, "%s", forbiddenError.Error()) } else { _ = json.Indent(formatted, output, "", " ") } w.Header().Set("Content-Type", restful.MIME_JSON) w.WriteHeader(http.StatusForbidden) w.Write(formatted.Bytes()) }
// RESTClientFor returns a RESTClient that satisfies the requested attributes on a client Config // object. func RESTClientFor(config *Config) (*RESTClient, error) { version := defaultVersionFor(config) // Set version versionInterfaces, err := latest.InterfacesFor(version) if err != nil { return nil, fmt.Errorf("API version '%s' is not recognized (valid values: %s)", version, strings.Join(latest.Versions, ", ")) } baseURL, err := defaultServerUrlFor(config) if err != nil { return nil, err } client := NewRESTClient(baseURL, versionInterfaces.Codec) transport, err := TransportFor(config) if err != nil { return nil, err } if transport != http.DefaultTransport { client.Client = &http.Client{Transport: transport} } return client, nil }
// NewEtcdStorage returns a StorageInterface for the provided arguments or an error if the version // is incorrect. func NewEtcdStorage(client tools.EtcdClient, version string, prefix string) (etcdStorage tools.StorageInterface, err error) { if version == "" { version = latest.Version } versionInterfaces, err := latest.InterfacesFor(version) if err != nil { return etcdStorage, err } return tools.NewEtcdStorage(client, versionInterfaces.Codec, prefix), nil }
// NewEtcdHelper returns an EtcdHelper for the provided arguments or an error if the version // is incorrect. func NewEtcdHelper(client tools.EtcdGetSet, version string, prefix string) (helper tools.EtcdHelper, err error) { if version == "" { version = latest.Version } versionInterfaces, err := latest.InterfacesFor(version) if err != nil { return helper, err } return tools.NewEtcdHelper(client, versionInterfaces.Codec, prefix), nil }
// NewEtcdHelper returns an EtcdHelper for the provided arguments or an error if the version // is incorrect. func NewEtcdHelper(client tools.EtcdGetSet, version string) (helper tools.EtcdHelper, err error) { if version == "" { version = latest.Version } versionInterfaces, err := latest.InterfacesFor(version) if err != nil { return helper, err } return tools.EtcdHelper{client, versionInterfaces.Codec, tools.RuntimeVersionAdapter{versionInterfaces.MetadataAccessor}}, nil }
// PrintObj is an implementation of ResourcePrinter.PrintObj which simply writes the object to the Writer. func (j *JSONPrinter) PrintObj(obj runtime.Object, w io.Writer) error { vi, err := latest.InterfacesFor(j.version) if err != nil { return err } data, err := vi.Codec.Encode(obj) if err != nil { return err } dst := bytes.Buffer{} err = json.Indent(&dst, data, "", " ") dst.WriteByte('\n') _, err = w.Write(dst.Bytes()) return err }
func toVersionedMap(version string, obj runtime.Object) (map[string]interface{}, error) { vi, err := latest.InterfacesFor(version) if err != nil { return nil, err } data, err := vi.Codec.Encode(obj) if err != nil { return nil, err } outObj := map[string]interface{}{} err = json.Unmarshal(data, &outObj) if err != nil { return nil, err } return outObj, nil }
// SetKubernetesDefaults sets default values on the provided client config for accessing the // Kubernetes API or returns an error if any of the defaults are impossible or invalid. func SetKubernetesDefaults(config *Config) error { if config.Prefix == "" { config.Prefix = "/api" } if len(config.Version) == 0 { config.Version = defaultVersionFor(config) } version := config.Version versionInterfaces, err := latest.InterfacesFor(version) if err != nil { return fmt.Errorf("API version '%s' is not recognized (valid values: %s)", version, strings.Join(latest.Versions, ", ")) } if config.Codec == nil { config.Codec = versionInterfaces.Codec } config.LegacyBehavior = (version == "v1beta1" || version == "v1beta2") return nil }
func Merge(dst runtime.Object, fragment, kind string) (runtime.Object, error) { // Ok, this is a little hairy, we'd rather not force the user to specify a kind for their JSON // So we pull it into a map, add the Kind field, and then reserialize. // We also pull the apiVersion for proper parsing var intermediate interface{} if err := json.Unmarshal([]byte(fragment), &intermediate); err != nil { return nil, err } dataMap, ok := intermediate.(map[string]interface{}) if !ok { return nil, fmt.Errorf("Expected a map, found something else: %s", fragment) } version, found := dataMap["apiVersion"] if !found { return nil, fmt.Errorf("Inline JSON requires an apiVersion field") } versionString, ok := version.(string) if !ok { return nil, fmt.Errorf("apiVersion must be a string") } i, err := latest.InterfacesFor(versionString) if err != nil { return nil, err } // encode dst into versioned json and apply fragment directly too it target, err := i.Codec.Encode(dst) if err != nil { return nil, err } patched, err := jsonpatch.MergePatch(target, []byte(fragment)) if err != nil { return nil, err } out, err := i.Codec.Decode(patched) if err != nil { return nil, err } return out, nil }
// TestProjectIsNamespace verifies that a project is a namespace, and a namespace is a project func TestProjectIsNamespace(t *testing.T) { testutil.DeleteAllEtcdKeys() etcdClient := testutil.NewEtcdClient() etcdHelper, err := master.NewEtcdStorage(etcdClient, latest.InterfacesFor, "v1", etcdtest.PathPrefix()) if err != nil { t.Fatalf("Unexpected error: %v", err) } // create a kube and its client kubeInterfaces, _ := klatest.InterfacesFor(klatest.Version) namespaceStorage, _, _ := namespaceetcd.NewStorage(etcdHelper) kubeStorage := map[string]rest.Storage{ "namespaces": namespaceStorage, } osMux := http.NewServeMux() server := httptest.NewServer(osMux) defer server.Close() handlerContainer := master.NewHandlerContainer(osMux) version := &apiserver.APIGroupVersion{ Root: "/api", Version: "v1beta3", Storage: kubeStorage, Codec: kv1beta3.Codec, Mapper: klatest.RESTMapper, Creater: kapi.Scheme, Typer: kapi.Scheme, Convertor: kapi.Scheme, Linker: kubeInterfaces.MetadataAccessor, Admit: admit.NewAlwaysAdmit(), Context: kapi.NewRequestContextMapper(), } if err := version.InstallREST(handlerContainer); err != nil { t.Fatalf("unable to install REST: %v", err) } kubeClient, err := kclient.New(&kclient.Config{Host: server.URL, Version: "v1beta3"}) if err != nil { t.Fatalf("Unexpected error: %v", err) } // create an origin originInterfaces, _ := latest.InterfacesFor(latest.Version) originStorage := map[string]rest.Storage{ "projects": projectregistry.NewREST(kubeClient.Namespaces(), nil), } osVersion := &apiserver.APIGroupVersion{ Root: "/oapi", Version: "v1", Storage: originStorage, Codec: latest.Codec, Mapper: latest.RESTMapper, Creater: kapi.Scheme, Typer: kapi.Scheme, Convertor: kapi.Scheme, Linker: originInterfaces.MetadataAccessor, Admit: admit.NewAlwaysAdmit(), Context: kapi.NewRequestContextMapper(), } if err := osVersion.InstallREST(handlerContainer); err != nil { t.Fatalf("unable to install REST: %v", err) } originClient, err := client.New(&kclient.Config{Host: server.URL}) if err != nil { t.Fatalf("unexpected error: %v", err) } // create a namespace namespace := &kapi.Namespace{ ObjectMeta: kapi.ObjectMeta{Name: "integration-test"}, } namespaceResult, err := kubeClient.Namespaces().Create(namespace) if err != nil { t.Fatalf("unexpected error: %v", err) } // now try to get the project with the same name and ensure it is our namespace project, err := originClient.Projects().Get(namespaceResult.Name) if err != nil { t.Fatalf("unexpected error: %v", err) } if project.Name != namespace.Name { t.Fatalf("Project name did not match namespace name, project %v, namespace %v", project.Name, namespace.Name) } // now create a project project = &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{ Name: "new-project", Annotations: map[string]string{ "openshift.io/display-name": "Hello World", "openshift.io/node-selector": "env=test", }, }, } projectResult, err := originClient.Projects().Create(project) if err != nil { t.Fatalf("unexpected error: %v", err) } // now get the namespace for that project namespace, err = kubeClient.Namespaces().Get(projectResult.Name) if err != nil { t.Fatalf("unexpected error: %v", err) } if project.Name != namespace.Name { t.Fatalf("Project name did not match namespace name, project %v, namespace %v", project.Name, namespace.Name) } if project.Annotations["openshift.io/display-name"] != namespace.Annotations["openshift.io/display-name"] { t.Fatalf("Project display name did not match namespace annotation, project %v, namespace %v", project.Annotations["openshift.io/display-name"], namespace.Annotations["openshift.io/display-name"]) } if project.Annotations["openshift.io/node-selector"] != namespace.Annotations["openshift.io/node-selector"] { t.Fatalf("Project node selector did not match namespace node selector, project %v, namespace %v", project.Annotations["openshift.io/node-selector"], namespace.Annotations["openshift.io/node-selector"]) } }