func TestLabelsForObject(t *testing.T) { f := NewFactory(nil) tests := []struct { name string object runtime.Object expected string err error }{ { name: "successful re-use of labels", object: &api.Service{ ObjectMeta: api.ObjectMeta{Name: "baz", Namespace: "test", Labels: map[string]string{"svc": "test"}}, TypeMeta: unversioned.TypeMeta{Kind: "Service", APIVersion: "v1"}, }, expected: "svc=test", err: nil, }, { name: "empty labels", object: &api.Service{ ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", Labels: map[string]string{}}, TypeMeta: unversioned.TypeMeta{Kind: "Service", APIVersion: "v1"}, }, expected: "", err: nil, }, { name: "nil labels", object: &api.Service{ ObjectMeta: api.ObjectMeta{Name: "zen", Namespace: "test", Labels: nil}, TypeMeta: unversioned.TypeMeta{Kind: "Service", APIVersion: "v1"}, }, expected: "", err: nil, }, } for _, test := range tests { gotLabels, err := f.LabelsForObject(test.object) if err != test.err { t.Fatalf("%s: Error mismatch: Expected %v, got %v", test.name, test.err, err) } got := kubectl.MakeLabels(gotLabels) if test.expected != got { t.Fatalf("%s: Labels mismatch! Expected %s, got %s", test.name, test.expected, got) } } }
func (f *ring0Factory) MapBasedSelectorForObject(object runtime.Object) (string, error) { // TODO: replace with a swagger schema based approach (identify pod selector via schema introspection) switch t := object.(type) { case *api.ReplicationController: return kubectl.MakeLabels(t.Spec.Selector), nil case *api.Pod: if len(t.Labels) == 0 { return "", fmt.Errorf("the pod has no labels and cannot be exposed") } return kubectl.MakeLabels(t.Labels), nil case *api.Service: if t.Spec.Selector == nil { return "", fmt.Errorf("the service has no pod selector set") } return kubectl.MakeLabels(t.Spec.Selector), nil case *extensions.Deployment: // TODO(madhusudancs): Make this smarter by admitting MatchExpressions with Equals // operator, DoubleEquals operator and In operator with only one element in the set. if len(t.Spec.Selector.MatchExpressions) > 0 { return "", fmt.Errorf("couldn't convert expressions - \"%+v\" to map-based selector format", t.Spec.Selector.MatchExpressions) } return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil case *extensions.ReplicaSet: // TODO(madhusudancs): Make this smarter by admitting MatchExpressions with Equals // operator, DoubleEquals operator and In operator with only one element in the set. if len(t.Spec.Selector.MatchExpressions) > 0 { return "", fmt.Errorf("couldn't convert expressions - \"%+v\" to map-based selector format", t.Spec.Selector.MatchExpressions) } return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil default: gvks, _, err := api.Scheme.ObjectKinds(object) if err != nil { return "", err } return "", fmt.Errorf("cannot extract pod selector from %v", gvks[0]) } }
// NewFactory creates a factory with the default Kubernetes resources defined // if optionalClientConfig is nil, then flags will be bound to a new clientcmd.ClientConfig. // if optionalClientConfig is not nil, then this factory will make use of it. func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { mapper := kubectl.ShortcutExpander{RESTMapper: registered.RESTMapper()} flags := pflag.NewFlagSet("", pflag.ContinueOnError) flags.SetNormalizeFunc(utilflag.WarnWordSepNormalizeFunc) // Warn for "_" flags clientConfig := optionalClientConfig if optionalClientConfig == nil { clientConfig = DefaultClientConfig(flags) } clients := NewClientCache(clientConfig) return &Factory{ clients: clients, flags: flags, Object: func() (meta.RESTMapper, runtime.ObjectTyper) { cfg, err := clientConfig.ClientConfig() CheckErr(err) cmdApiVersion := unversioned.GroupVersion{} if cfg.GroupVersion != nil { cmdApiVersion = *cfg.GroupVersion } outputRESTMapper := kubectl.OutputVersionMapper{RESTMapper: mapper, OutputVersions: []unversioned.GroupVersion{cmdApiVersion}} // eventually this should allow me choose a group priority based on the order of the discovery doc, for now hardcode a given order priorityRESTMapper := meta.PriorityRESTMapper{ Delegate: outputRESTMapper, ResourcePriority: []unversioned.GroupVersionResource{ {Group: api.GroupName, Version: meta.AnyVersion, Resource: meta.AnyResource}, {Group: extensions.GroupName, Version: meta.AnyVersion, Resource: meta.AnyResource}, {Group: metrics.GroupName, Version: meta.AnyVersion, Resource: meta.AnyResource}, }, KindPriority: []unversioned.GroupVersionKind{ {Group: api.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind}, {Group: extensions.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind}, {Group: metrics.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind}, }, } return priorityRESTMapper, api.Scheme }, Client: func() (*client.Client, error) { return clients.ClientForVersion(nil) }, ClientConfig: func() (*restclient.Config, error) { return clients.ClientConfigForVersion(nil) }, ClientForMapping: func(mapping *meta.RESTMapping) (resource.RESTClient, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() client, err := clients.ClientForVersion(&mappingVersion) if err != nil { return nil, err } switch mapping.GroupVersionKind.Group { case api.GroupName: return client.RESTClient, nil case autoscaling.GroupName: return client.AutoscalingClient.RESTClient, nil case batch.GroupName: return client.BatchClient.RESTClient, nil case extensions.GroupName: return client.ExtensionsClient.RESTClient, nil } return nil, fmt.Errorf("unable to get RESTClient for resource '%s'", mapping.Resource) }, Describer: func(mapping *meta.RESTMapping) (kubectl.Describer, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() client, err := clients.ClientForVersion(&mappingVersion) if err != nil { return nil, err } if describer, ok := kubectl.DescriberFor(mapping.GroupVersionKind.GroupKind(), client); ok { return describer, nil } return nil, fmt.Errorf("no description has been implemented for %q", mapping.GroupVersionKind.Kind) }, Decoder: func(toInternal bool) runtime.Decoder { if toInternal { return api.Codecs.UniversalDecoder() } return api.Codecs.UniversalDeserializer() }, JSONEncoder: func() runtime.Encoder { return api.Codecs.LegacyCodec(registered.EnabledVersions()...) }, Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll bool, showLabels bool, absoluteTimestamps bool, columnLabels []string) (kubectl.ResourcePrinter, error) { return kubectl.NewHumanReadablePrinter(noHeaders, withNamespace, wide, showAll, showLabels, absoluteTimestamps, columnLabels), nil }, PodSelectorForObject: func(object runtime.Object) (string, error) { // TODO: replace with a swagger schema based approach (identify pod selector via schema introspection) switch t := object.(type) { case *api.ReplicationController: return kubectl.MakeLabels(t.Spec.Selector), nil case *api.Pod: if len(t.Labels) == 0 { return "", fmt.Errorf("the pod has no labels and cannot be exposed") } return kubectl.MakeLabels(t.Labels), nil case *api.Service: if t.Spec.Selector == nil { return "", fmt.Errorf("the service has no pod selector set") } return kubectl.MakeLabels(t.Spec.Selector), nil case *extensions.Deployment: selector, err := unversioned.LabelSelectorAsSelector(t.Spec.Selector) if err != nil { return "", fmt.Errorf("invalid label selector: %v", err) } return selector.String(), nil case *extensions.ReplicaSet: selector, err := unversioned.LabelSelectorAsSelector(t.Spec.Selector) if err != nil { return "", fmt.Errorf("failed to convert label selector to selector: %v", err) } return selector.String(), nil default: gvk, err := api.Scheme.ObjectKind(object) if err != nil { return "", err } return "", fmt.Errorf("cannot extract pod selector from %v", gvk) } }, MapBasedSelectorForObject: func(object runtime.Object) (string, error) { // TODO: replace with a swagger schema based approach (identify pod selector via schema introspection) switch t := object.(type) { case *api.ReplicationController: return kubectl.MakeLabels(t.Spec.Selector), nil case *api.Pod: if len(t.Labels) == 0 { return "", fmt.Errorf("the pod has no labels and cannot be exposed") } return kubectl.MakeLabels(t.Labels), nil case *api.Service: if t.Spec.Selector == nil { return "", fmt.Errorf("the service has no pod selector set") } return kubectl.MakeLabels(t.Spec.Selector), nil case *extensions.Deployment: // TODO(madhusudancs): Make this smarter by admitting MatchExpressions with Equals // operator, DoubleEquals operator and In operator with only one element in the set. if len(t.Spec.Selector.MatchExpressions) > 0 { return "", fmt.Errorf("couldn't convert expressions - \"%+v\" to map-based selector format") } return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil case *extensions.ReplicaSet: // TODO(madhusudancs): Make this smarter by admitting MatchExpressions with Equals // operator, DoubleEquals operator and In operator with only one element in the set. if len(t.Spec.Selector.MatchExpressions) > 0 { return "", fmt.Errorf("couldn't convert expressions - \"%+v\" to map-based selector format") } return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil default: gvk, err := api.Scheme.ObjectKind(object) if err != nil { return "", err } return "", fmt.Errorf("cannot extract pod selector from %v", gvk) } }, PortsForObject: func(object runtime.Object) ([]string, error) { // TODO: replace with a swagger schema based approach (identify pod selector via schema introspection) switch t := object.(type) { case *api.ReplicationController: return getPorts(t.Spec.Template.Spec), nil case *api.Pod: return getPorts(t.Spec), nil case *api.Service: return getServicePorts(t.Spec), nil case *extensions.Deployment: return getPorts(t.Spec.Template.Spec), nil case *extensions.ReplicaSet: return getPorts(t.Spec.Template.Spec), nil default: gvk, err := api.Scheme.ObjectKind(object) if err != nil { return nil, err } return nil, fmt.Errorf("cannot extract ports from %v", gvk) } }, LabelsForObject: func(object runtime.Object) (map[string]string, error) { return meta.NewAccessor().Labels(object) }, LogsForObject: func(object, options runtime.Object) (*restclient.Request, error) { c, err := clients.ClientForVersion(nil) if err != nil { return nil, err } switch t := object.(type) { case *api.Pod: opts, ok := options.(*api.PodLogOptions) if !ok { return nil, errors.New("provided options object is not a PodLogOptions") } return c.Pods(t.Namespace).GetLogs(t.Name, opts), nil case *api.ReplicationController: opts, ok := options.(*api.PodLogOptions) if !ok { return nil, errors.New("provided options object is not a PodLogOptions") } selector := labels.SelectorFromSet(t.Spec.Selector) pod, numPods, err := GetFirstPod(c, t.Namespace, selector) if err != nil { return nil, err } if numPods > 1 { fmt.Fprintf(os.Stderr, "Found %v pods, using pod/%v\n", numPods, pod.Name) } return c.Pods(pod.Namespace).GetLogs(pod.Name, opts), nil case *extensions.ReplicaSet: opts, ok := options.(*api.PodLogOptions) if !ok { return nil, errors.New("provided options object is not a PodLogOptions") } selector, err := unversioned.LabelSelectorAsSelector(t.Spec.Selector) if err != nil { return nil, fmt.Errorf("invalid label selector: %v", err) } pod, numPods, err := GetFirstPod(c, t.Namespace, selector) if err != nil { return nil, err } if numPods > 1 { fmt.Fprintf(os.Stderr, "Found %v pods, using pod/%v\n", numPods, pod.Name) } return c.Pods(pod.Namespace).GetLogs(pod.Name, opts), nil default: gvk, err := api.Scheme.ObjectKind(object) if err != nil { return nil, err } return nil, fmt.Errorf("cannot get the logs from %v", gvk) } }, PauseObject: func(object runtime.Object) (bool, error) { c, err := clients.ClientForVersion(nil) if err != nil { return false, err } switch t := object.(type) { case *extensions.Deployment: if t.Spec.Paused { return true, nil } t.Spec.Paused = true _, err := c.Extensions().Deployments(t.Namespace).Update(t) return false, err default: gvk, err := api.Scheme.ObjectKind(object) if err != nil { return false, err } return false, fmt.Errorf("cannot pause %v", gvk) } }, ResumeObject: func(object runtime.Object) (bool, error) { c, err := clients.ClientForVersion(nil) if err != nil { return false, err } switch t := object.(type) { case *extensions.Deployment: if !t.Spec.Paused { return true, nil } t.Spec.Paused = false _, err := c.Extensions().Deployments(t.Namespace).Update(t) return false, err default: gvk, err := api.Scheme.ObjectKind(object) if err != nil { return false, err } return false, fmt.Errorf("cannot resume %v", gvk) } }, Scaler: func(mapping *meta.RESTMapping) (kubectl.Scaler, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() client, err := clients.ClientForVersion(&mappingVersion) if err != nil { return nil, err } return kubectl.ScalerFor(mapping.GroupVersionKind.GroupKind(), client) }, Reaper: func(mapping *meta.RESTMapping) (kubectl.Reaper, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() client, err := clients.ClientForVersion(&mappingVersion) if err != nil { return nil, err } return kubectl.ReaperFor(mapping.GroupVersionKind.GroupKind(), client) }, HistoryViewer: func(mapping *meta.RESTMapping) (kubectl.HistoryViewer, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() client, err := clients.ClientForVersion(&mappingVersion) clientset := clientset.FromUnversionedClient(client) if err != nil { return nil, err } return kubectl.HistoryViewerFor(mapping.GroupVersionKind.GroupKind(), clientset) }, Rollbacker: func(mapping *meta.RESTMapping) (kubectl.Rollbacker, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() client, err := clients.ClientForVersion(&mappingVersion) if err != nil { return nil, err } return kubectl.RollbackerFor(mapping.GroupVersionKind.GroupKind(), client) }, Validator: func(validate bool, cacheDir string) (validation.Schema, error) { if validate { client, err := clients.ClientForVersion(nil) if err != nil { return nil, err } dir := cacheDir if len(dir) > 0 { version, err := client.ServerVersion() if err != nil { return nil, err } dir = path.Join(cacheDir, version.String()) } return &clientSwaggerSchema{ c: client, cacheDir: dir, mapper: api.RESTMapper, }, nil } return validation.NullSchema{}, nil }, SwaggerSchema: func(gvk unversioned.GroupVersionKind) (*swagger.ApiDeclaration, error) { version := gvk.GroupVersion() client, err := clients.ClientForVersion(&version) if err != nil { return nil, err } return client.Discovery().SwaggerSchema(version) }, DefaultNamespace: func() (string, bool, error) { return clientConfig.Namespace() }, Generators: func(cmdName string) map[string]kubectl.Generator { return DefaultGenerators(cmdName) }, CanBeExposed: func(kind unversioned.GroupKind) error { switch kind { case api.Kind("ReplicationController"), api.Kind("Service"), api.Kind("Pod"), extensions.Kind("Deployment"), extensions.Kind("ReplicaSet"): // nothing to do here default: return fmt.Errorf("cannot expose a %s", kind) } return nil }, CanBeAutoscaled: func(kind unversioned.GroupKind) error { switch kind { case api.Kind("ReplicationController"), extensions.Kind("Deployment"), extensions.Kind("ReplicaSet"): // nothing to do here default: return fmt.Errorf("cannot autoscale a %v", kind) } return nil }, AttachablePodForObject: func(object runtime.Object) (*api.Pod, error) { client, err := clients.ClientForVersion(nil) if err != nil { return nil, err } switch t := object.(type) { case *api.ReplicationController: selector := labels.SelectorFromSet(t.Spec.Selector) pod, _, err := GetFirstPod(client, t.Namespace, selector) return pod, err case *extensions.Deployment: selector, err := unversioned.LabelSelectorAsSelector(t.Spec.Selector) if err != nil { return nil, fmt.Errorf("invalid label selector: %v", err) } pod, _, err := GetFirstPod(client, t.Namespace, selector) return pod, err case *extensions.Job: selector, err := unversioned.LabelSelectorAsSelector(t.Spec.Selector) if err != nil { return nil, fmt.Errorf("invalid label selector: %v", err) } pod, _, err := GetFirstPod(client, t.Namespace, selector) return pod, err case *api.Pod: return t, nil default: gvk, err := api.Scheme.ObjectKind(object) if err != nil { return nil, err } return nil, fmt.Errorf("cannot attach to %v: not implemented", gvk) } }, EditorEnvs: func() []string { return []string{"KUBE_EDITOR", "EDITOR"} }, } }
// NewFactory creates an object that holds common methods across all OpenShift commands func NewFactory(clientConfig kclientcmd.ClientConfig) *Factory { restMapper := registered.RESTMapper() clients := &clientCache{ clients: make(map[string]*client.Client), configs: make(map[string]*restclient.Config), loader: clientConfig, } w := &Factory{ Factory: cmdutil.NewFactory(clientConfig), OpenShiftClientConfig: clientConfig, clients: clients, ImageResolutionOptions: &imageResolutionOptions{}, } w.Object = func(bool) (meta.RESTMapper, runtime.ObjectTyper) { defaultMapper := ShortcutExpander{RESTMapper: kubectl.ShortcutExpander{RESTMapper: restMapper}} defaultTyper := api.Scheme // Output using whatever version was negotiated in the client cache. The // version we decode with may not be the same as what the server requires. cfg, err := clients.ClientConfigForVersion(nil) if err != nil { return defaultMapper, defaultTyper } cmdApiVersion := unversioned.GroupVersion{} if cfg.GroupVersion != nil { cmdApiVersion = *cfg.GroupVersion } // at this point we've negotiated and can get the client oclient, err := clients.ClientForVersion(nil) if err != nil { return defaultMapper, defaultTyper } cacheDir := computeDiscoverCacheDir(filepath.Join(homedir.HomeDir(), ".kube"), cfg.Host) cachedDiscoverClient := NewCachedDiscoveryClient(client.NewDiscoveryClient(oclient.RESTClient), cacheDir, time.Duration(10*time.Minute)) // if we can't find the server version or its too old to have Kind information in the discovery doc, skip the discovery RESTMapper // and use our hardcoded levels mapper := registered.RESTMapper() if serverVersion, err := cachedDiscoverClient.ServerVersion(); err == nil && useDiscoveryRESTMapper(serverVersion.GitVersion) { mapper = restmapper.NewDiscoveryRESTMapper(cachedDiscoverClient) } mapper = NewShortcutExpander(cachedDiscoverClient, kubectl.ShortcutExpander{RESTMapper: mapper}) return kubectl.OutputVersionMapper{RESTMapper: mapper, OutputVersions: []unversioned.GroupVersion{cmdApiVersion}}, api.Scheme } w.UnstructuredObject = func() (meta.RESTMapper, runtime.ObjectTyper, error) { // load a discovery client from the default config cfg, err := clients.ClientConfigForVersion(nil) if err != nil { return nil, nil, err } dc, err := discovery.NewDiscoveryClientForConfig(cfg) if err != nil { return nil, nil, err } cacheDir := computeDiscoverCacheDir(filepath.Join(homedir.HomeDir(), ".kube"), cfg.Host) cachedDiscoverClient := NewCachedDiscoveryClient(client.NewDiscoveryClient(dc.RESTClient), cacheDir, time.Duration(10*time.Minute)) // enumerate all group resources groupResources, err := discovery.GetAPIGroupResources(cachedDiscoverClient) if err != nil { return nil, nil, err } // Register unknown APIs as third party for now to make // validation happy. TODO perhaps make a dynamic schema // validator to avoid this. for _, group := range groupResources { for _, version := range group.Group.Versions { gv := unversioned.GroupVersion{Group: group.Group.Name, Version: version.Version} if !registered.IsRegisteredVersion(gv) { registered.AddThirdPartyAPIGroupVersions(gv) } } } // construct unstructured mapper and typer mapper := discovery.NewRESTMapper(groupResources, meta.InterfacesForUnstructured) typer := discovery.NewUnstructuredObjectTyper(groupResources) return NewShortcutExpander(cachedDiscoverClient, kubectl.ShortcutExpander{RESTMapper: mapper}), typer, nil } kClientForMapping := w.Factory.ClientForMapping w.ClientForMapping = func(mapping *meta.RESTMapping) (resource.RESTClient, error) { if latest.OriginKind(mapping.GroupVersionKind) { mappingVersion := mapping.GroupVersionKind.GroupVersion() client, err := clients.ClientForVersion(&mappingVersion) if err != nil { return nil, err } return client.RESTClient, nil } return kClientForMapping(mapping) } kUnstructuredClientForMapping := w.Factory.UnstructuredClientForMapping w.UnstructuredClientForMapping = func(mapping *meta.RESTMapping) (resource.RESTClient, error) { if latest.OriginKind(mapping.GroupVersionKind) { cfg, err := clientConfig.ClientConfig() if err != nil { return nil, err } if err := client.SetOpenShiftDefaults(cfg); err != nil { return nil, err } cfg.APIPath = "/apis" if mapping.GroupVersionKind.Group == api.GroupName { cfg.APIPath = "/oapi" } gv := mapping.GroupVersionKind.GroupVersion() cfg.ContentConfig = dynamic.ContentConfig() cfg.GroupVersion = &gv return restclient.RESTClientFor(cfg) } return kUnstructuredClientForMapping(mapping) } // Save original Describer function kDescriberFunc := w.Factory.Describer w.Describer = func(mapping *meta.RESTMapping) (kubectl.Describer, error) { if latest.OriginKind(mapping.GroupVersionKind) { oClient, kClient, err := w.Clients() if err != nil { return nil, fmt.Errorf("unable to create client %s: %v", mapping.GroupVersionKind.Kind, err) } mappingVersion := mapping.GroupVersionKind.GroupVersion() cfg, err := clients.ClientConfigForVersion(&mappingVersion) if err != nil { return nil, fmt.Errorf("unable to load a client %s: %v", mapping.GroupVersionKind.Kind, err) } describer, ok := describe.DescriberFor(mapping.GroupVersionKind.GroupKind(), oClient, kClient, cfg.Host) if !ok { return nil, fmt.Errorf("no description has been implemented for %q", mapping.GroupVersionKind.Kind) } return describer, nil } return kDescriberFunc(mapping) } kScalerFunc := w.Factory.Scaler w.Scaler = func(mapping *meta.RESTMapping) (kubectl.Scaler, error) { if mapping.GroupVersionKind.GroupKind() == deployapi.Kind("DeploymentConfig") { oc, kc, err := w.Clients() if err != nil { return nil, err } return deploycmd.NewDeploymentConfigScaler(oc, kc), nil } return kScalerFunc(mapping) } kReaperFunc := w.Factory.Reaper w.Reaper = func(mapping *meta.RESTMapping) (kubectl.Reaper, error) { switch mapping.GroupVersionKind.GroupKind() { case deployapi.Kind("DeploymentConfig"): oc, kc, err := w.Clients() if err != nil { return nil, err } return deploycmd.NewDeploymentConfigReaper(oc, kc), nil case authorizationapi.Kind("Role"): oc, _, err := w.Clients() if err != nil { return nil, err } return authorizationreaper.NewRoleReaper(oc, oc), nil case authorizationapi.Kind("ClusterRole"): oc, _, err := w.Clients() if err != nil { return nil, err } return authorizationreaper.NewClusterRoleReaper(oc, oc, oc), nil case userapi.Kind("User"): oc, kc, err := w.Clients() if err != nil { return nil, err } return authenticationreaper.NewUserReaper( client.UsersInterface(oc), client.GroupsInterface(oc), client.ClusterRoleBindingsInterface(oc), client.RoleBindingsNamespacer(oc), kclient.SecurityContextConstraintsInterface(kc), ), nil case userapi.Kind("Group"): oc, kc, err := w.Clients() if err != nil { return nil, err } return authenticationreaper.NewGroupReaper( client.GroupsInterface(oc), client.ClusterRoleBindingsInterface(oc), client.RoleBindingsNamespacer(oc), kclient.SecurityContextConstraintsInterface(kc), ), nil case buildapi.Kind("BuildConfig"): oc, _, err := w.Clients() if err != nil { return nil, err } return buildcmd.NewBuildConfigReaper(oc), nil } return kReaperFunc(mapping) } kGenerators := w.Factory.Generators w.Generators = func(cmdName string) map[string]kubectl.Generator { originGenerators := DefaultGenerators(cmdName) kubeGenerators := kGenerators(cmdName) ret := map[string]kubectl.Generator{} for k, v := range kubeGenerators { ret[k] = v } for k, v := range originGenerators { ret[k] = v } return ret } kMapBasedSelectorForObjectFunc := w.Factory.MapBasedSelectorForObject w.MapBasedSelectorForObject = func(object runtime.Object) (string, error) { switch t := object.(type) { case *deployapi.DeploymentConfig: return kubectl.MakeLabels(t.Spec.Selector), nil default: return kMapBasedSelectorForObjectFunc(object) } } kPortsForObjectFunc := w.Factory.PortsForObject w.PortsForObject = func(object runtime.Object) ([]string, error) { switch t := object.(type) { case *deployapi.DeploymentConfig: return getPorts(t.Spec.Template.Spec), nil default: return kPortsForObjectFunc(object) } } kLogsForObjectFunc := w.Factory.LogsForObject w.LogsForObject = func(object, options runtime.Object) (*restclient.Request, error) { switch t := object.(type) { case *deployapi.DeploymentConfig: dopts, ok := options.(*deployapi.DeploymentLogOptions) if !ok { return nil, errors.New("provided options object is not a DeploymentLogOptions") } oc, _, err := w.Clients() if err != nil { return nil, err } return oc.DeploymentLogs(t.Namespace).Get(t.Name, *dopts), nil case *buildapi.Build: bopts, ok := options.(*buildapi.BuildLogOptions) if !ok { return nil, errors.New("provided options object is not a BuildLogOptions") } if bopts.Version != nil { return nil, errors.New("cannot specify a version and a build") } oc, _, err := w.Clients() if err != nil { return nil, err } return oc.BuildLogs(t.Namespace).Get(t.Name, *bopts), nil case *buildapi.BuildConfig: bopts, ok := options.(*buildapi.BuildLogOptions) if !ok { return nil, errors.New("provided options object is not a BuildLogOptions") } oc, _, err := w.Clients() if err != nil { return nil, err } builds, err := oc.Builds(t.Namespace).List(api.ListOptions{}) if err != nil { return nil, err } builds.Items = buildapi.FilterBuilds(builds.Items, buildapi.ByBuildConfigPredicate(t.Name)) if len(builds.Items) == 0 { return nil, fmt.Errorf("no builds found for %q", t.Name) } if bopts.Version != nil { // If a version has been specified, try to get the logs from that build. desired := buildutil.BuildNameForConfigVersion(t.Name, int(*bopts.Version)) return oc.BuildLogs(t.Namespace).Get(desired, *bopts), nil } sort.Sort(sort.Reverse(buildapi.BuildSliceByCreationTimestamp(builds.Items))) return oc.BuildLogs(t.Namespace).Get(builds.Items[0].Name, *bopts), nil default: return kLogsForObjectFunc(object, options) } } // Saves current resource name (or alias if any) in PrintOptions. Once saved, it will not be overwritten by the // kubernetes resource alias look-up, as it will notice a non-empty value in `options.Kind` w.Printer = func(mapping *meta.RESTMapping, options kubectl.PrintOptions) (kubectl.ResourcePrinter, error) { if mapping != nil { options.Kind = mapping.Resource if alias, ok := resourceShortFormFor(mapping.Resource); ok { options.Kind = alias } } return describe.NewHumanReadablePrinter(options), nil } // PrintResourceInfos receives a list of resource infos and prints versioned objects if a generic output format was specified // otherwise, it iterates through info objects, printing each resource with a unique printer for its mapping w.PrintResourceInfos = func(cmd *cobra.Command, infos []*resource.Info, out io.Writer) error { printer, generic, err := cmdutil.PrinterForCommand(cmd) if err != nil { return nil } if !generic { for _, info := range infos { mapping := info.ResourceMapping() printer, err := w.PrinterForMapping(cmd, mapping, false) if err != nil { return err } if err := printer.PrintObj(info.Object, out); err != nil { return nil } } return nil } clientConfig, err := w.ClientConfig() if err != nil { return err } outputVersion, err := cmdutil.OutputVersion(cmd, clientConfig.GroupVersion) if err != nil { return err } object, err := resource.AsVersionedObject(infos, len(infos) != 1, outputVersion, api.Codecs.LegacyCodec(outputVersion)) if err != nil { return err } return printer.PrintObj(object, out) } kCanBeExposed := w.Factory.CanBeExposed w.CanBeExposed = func(kind unversioned.GroupKind) error { if kind == deployapi.Kind("DeploymentConfig") { return nil } return kCanBeExposed(kind) } kCanBeAutoscaled := w.Factory.CanBeAutoscaled w.CanBeAutoscaled = func(kind unversioned.GroupKind) error { if kind == deployapi.Kind("DeploymentConfig") { return nil } return kCanBeAutoscaled(kind) } kAttachablePodForObjectFunc := w.Factory.AttachablePodForObject w.AttachablePodForObject = func(object runtime.Object) (*api.Pod, error) { switch t := object.(type) { case *deployapi.DeploymentConfig: _, kc, err := w.Clients() if err != nil { return nil, err } selector := labels.SelectorFromSet(t.Spec.Selector) f := func(pods []*api.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) } pod, _, err := cmdutil.GetFirstPod(kc, t.Namespace, selector, 1*time.Minute, f) return pod, err default: return kAttachablePodForObjectFunc(object) } } kUpdatePodSpecForObject := w.Factory.UpdatePodSpecForObject w.UpdatePodSpecForObject = func(obj runtime.Object, fn func(*api.PodSpec) error) (bool, error) { switch t := obj.(type) { case *deployapi.DeploymentConfig: template := t.Spec.Template if template == nil { t.Spec.Template = template template = &api.PodTemplateSpec{} } return true, fn(&template.Spec) default: return kUpdatePodSpecForObject(obj, fn) } } kProtocolsForObject := w.Factory.ProtocolsForObject w.ProtocolsForObject = func(object runtime.Object) (map[string]string, error) { switch t := object.(type) { case *deployapi.DeploymentConfig: return getProtocols(t.Spec.Template.Spec), nil default: return kProtocolsForObject(object) } } kSwaggerSchemaFunc := w.Factory.SwaggerSchema w.Factory.SwaggerSchema = func(gvk unversioned.GroupVersionKind) (*swagger.ApiDeclaration, error) { if !latest.OriginKind(gvk) { return kSwaggerSchemaFunc(gvk) } // TODO: we need to register the OpenShift API under the Kube group, and start returning the OpenShift // group from the scheme. oc, _, err := w.Clients() if err != nil { return nil, err } return w.OriginSwaggerSchema(oc.RESTClient, gvk.GroupVersion()) } w.EditorEnvs = func() []string { return []string{"OC_EDITOR", "EDITOR"} } w.PrintObjectSpecificMessage = func(obj runtime.Object, out io.Writer) {} kPauseObjectFunc := w.Factory.PauseObject w.Factory.PauseObject = func(object runtime.Object) (bool, error) { switch t := object.(type) { case *deployapi.DeploymentConfig: if t.Spec.Paused { return true, nil } t.Spec.Paused = true oc, _, err := w.Clients() if err != nil { return false, err } _, err = oc.DeploymentConfigs(t.Namespace).Update(t) // TODO: Pause the deployer containers. return false, err default: return kPauseObjectFunc(object) } } kResumeObjectFunc := w.Factory.ResumeObject w.Factory.ResumeObject = func(object runtime.Object) (bool, error) { switch t := object.(type) { case *deployapi.DeploymentConfig: if !t.Spec.Paused { return true, nil } t.Spec.Paused = false oc, _, err := w.Clients() if err != nil { return false, err } _, err = oc.DeploymentConfigs(t.Namespace).Update(t) // TODO: Resume the deployer containers. return false, err default: return kResumeObjectFunc(object) } } kResolveImageFunc := w.Factory.ResolveImage w.Factory.ResolveImage = func(image string) (string, error) { options := w.ImageResolutionOptions.(*imageResolutionOptions) if imageutil.IsDocker(options.Source) { return kResolveImageFunc(image) } oc, _, err := w.Clients() if err != nil { return "", err } namespace, _, err := w.DefaultNamespace() if err != nil { return "", err } return imageutil.ResolveImagePullSpec(oc, oc, options.Source, image, namespace) } kHistoryViewerFunc := w.Factory.HistoryViewer w.Factory.HistoryViewer = func(mapping *meta.RESTMapping) (kubectl.HistoryViewer, error) { switch mapping.GroupVersionKind.GroupKind() { case deployapi.Kind("DeploymentConfig"): oc, kc, err := w.Clients() if err != nil { return nil, err } return deploycmd.NewDeploymentConfigHistoryViewer(oc, kc), nil } return kHistoryViewerFunc(mapping) } kRollbackerFunc := w.Factory.Rollbacker w.Factory.Rollbacker = func(mapping *meta.RESTMapping) (kubectl.Rollbacker, error) { switch mapping.GroupVersionKind.GroupKind() { case deployapi.Kind("DeploymentConfig"): oc, _, err := w.Clients() if err != nil { return nil, err } return deploycmd.NewDeploymentConfigRollbacker(oc), nil } return kRollbackerFunc(mapping) } kStatusViewerFunc := w.Factory.StatusViewer w.Factory.StatusViewer = func(mapping *meta.RESTMapping) (kubectl.StatusViewer, error) { oc, _, err := w.Clients() if err != nil { return nil, err } switch mapping.GroupVersionKind.GroupKind() { case deployapi.Kind("DeploymentConfig"): return deploycmd.NewDeploymentConfigStatusViewer(oc), nil } return kStatusViewerFunc(mapping) } return w }
// NewFactory creates a factory with the default Kubernetes resources defined // if optionalClientConfig is nil, then flags will be bound to a new clientcmd.ClientConfig. // if optionalClientConfig is not nil, then this factory will make use of it. func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { mapper := kubectl.ShortcutExpander{RESTMapper: registered.RESTMapper()} flags := pflag.NewFlagSet("", pflag.ContinueOnError) flags.SetNormalizeFunc(utilflag.WarnWordSepNormalizeFunc) // Warn for "_" flags clientConfig := optionalClientConfig if optionalClientConfig == nil { clientConfig = DefaultClientConfig(flags) } clients := NewClientCache(clientConfig) return &Factory{ clients: clients, flags: flags, // If discoverDynamicAPIs is true, make API calls to the discovery service to find APIs that // have been dynamically added to the apiserver Object: func(discoverDynamicAPIs bool) (meta.RESTMapper, runtime.ObjectTyper) { cfg, err := clientConfig.ClientConfig() CheckErr(err) cmdApiVersion := unversioned.GroupVersion{} if cfg.GroupVersion != nil { cmdApiVersion = *cfg.GroupVersion } if discoverDynamicAPIs { client, err := clients.ClientForVersion(&unversioned.GroupVersion{Version: "v1"}) CheckErr(err) versions, gvks, err := GetThirdPartyGroupVersions(client.Discovery()) CheckErr(err) if len(versions) > 0 { priorityMapper, ok := mapper.RESTMapper.(meta.PriorityRESTMapper) if !ok { CheckErr(fmt.Errorf("expected PriorityMapper, saw: %v", mapper.RESTMapper)) return nil, nil } multiMapper, ok := priorityMapper.Delegate.(meta.MultiRESTMapper) if !ok { CheckErr(fmt.Errorf("unexpected type: %v", mapper.RESTMapper)) return nil, nil } groupsMap := map[string][]unversioned.GroupVersion{} for _, version := range versions { groupsMap[version.Group] = append(groupsMap[version.Group], version) } for group, versionList := range groupsMap { preferredExternalVersion := versionList[0] thirdPartyMapper, err := kubectl.NewThirdPartyResourceMapper(versionList, getGroupVersionKinds(gvks, group)) CheckErr(err) accessor := meta.NewAccessor() groupMeta := apimachinery.GroupMeta{ GroupVersion: preferredExternalVersion, GroupVersions: versionList, RESTMapper: thirdPartyMapper, SelfLinker: runtime.SelfLinker(accessor), InterfacesFor: makeInterfacesFor(versionList), } CheckErr(registered.RegisterGroup(groupMeta)) registered.AddThirdPartyAPIGroupVersions(versionList...) multiMapper = append(meta.MultiRESTMapper{thirdPartyMapper}, multiMapper...) } priorityMapper.Delegate = multiMapper // Re-assign to the RESTMapper here because priorityMapper is actually a copy, so if we // don't re-assign, the above assignement won't actually update mapper.RESTMapper mapper.RESTMapper = priorityMapper } } outputRESTMapper := kubectl.OutputVersionMapper{RESTMapper: mapper, OutputVersions: []unversioned.GroupVersion{cmdApiVersion}} priorityRESTMapper := meta.PriorityRESTMapper{ Delegate: outputRESTMapper, ResourcePriority: []unversioned.GroupVersionResource{ {Group: api.GroupName, Version: meta.AnyVersion, Resource: meta.AnyResource}, {Group: extensions.GroupName, Version: meta.AnyVersion, Resource: meta.AnyResource}, {Group: metrics.GroupName, Version: meta.AnyVersion, Resource: meta.AnyResource}, }, KindPriority: []unversioned.GroupVersionKind{ {Group: api.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind}, {Group: extensions.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind}, {Group: metrics.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind}, }, } return priorityRESTMapper, api.Scheme }, Client: func() (*client.Client, error) { return clients.ClientForVersion(nil) }, ClientConfig: func() (*restclient.Config, error) { return clients.ClientConfigForVersion(nil) }, ClientForMapping: func(mapping *meta.RESTMapping) (resource.RESTClient, error) { gvk := mapping.GroupVersionKind mappingVersion := mapping.GroupVersionKind.GroupVersion() c, err := clients.ClientForVersion(&mappingVersion) if err != nil { return nil, err } switch gvk.Group { case api.GroupName: return c.RESTClient, nil case autoscaling.GroupName: return c.AutoscalingClient.RESTClient, nil case batch.GroupName: return c.BatchClient.RESTClient, nil case apps.GroupName: return c.AppsClient.RESTClient, nil case extensions.GroupName: return c.ExtensionsClient.RESTClient, nil case api.SchemeGroupVersion.Group: return c.RESTClient, nil case extensions.SchemeGroupVersion.Group: return c.ExtensionsClient.RESTClient, nil default: if !registered.IsThirdPartyAPIGroupVersion(gvk.GroupVersion()) { return nil, fmt.Errorf("unknown api group/version: %s", gvk.String()) } cfg, err := clientConfig.ClientConfig() if err != nil { return nil, err } gv := gvk.GroupVersion() cfg.GroupVersion = &gv cfg.APIPath = "/apis" cfg.Codec = thirdpartyresourcedata.NewCodec(c.ExtensionsClient.RESTClient.Codec(), gvk.Kind) return restclient.RESTClientFor(cfg) } }, Describer: func(mapping *meta.RESTMapping) (kubectl.Describer, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() client, err := clients.ClientForVersion(&mappingVersion) if err != nil { return nil, err } if describer, ok := kubectl.DescriberFor(mapping.GroupVersionKind.GroupKind(), client); ok { return describer, nil } return nil, fmt.Errorf("no description has been implemented for %q", mapping.GroupVersionKind.Kind) }, Decoder: func(toInternal bool) runtime.Decoder { if toInternal { return api.Codecs.UniversalDecoder() } return api.Codecs.UniversalDeserializer() }, JSONEncoder: func() runtime.Encoder { return api.Codecs.LegacyCodec(registered.EnabledVersions()...) }, Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll bool, showLabels bool, absoluteTimestamps bool, columnLabels []string) (kubectl.ResourcePrinter, error) { return kubectl.NewHumanReadablePrinter(noHeaders, withNamespace, wide, showAll, showLabels, absoluteTimestamps, columnLabels), nil }, MapBasedSelectorForObject: func(object runtime.Object) (string, error) { // TODO: replace with a swagger schema based approach (identify pod selector via schema introspection) switch t := object.(type) { case *api.ReplicationController: return kubectl.MakeLabels(t.Spec.Selector), nil case *api.Pod: if len(t.Labels) == 0 { return "", fmt.Errorf("the pod has no labels and cannot be exposed") } return kubectl.MakeLabels(t.Labels), nil case *api.Service: if t.Spec.Selector == nil { return "", fmt.Errorf("the service has no pod selector set") } return kubectl.MakeLabels(t.Spec.Selector), nil case *extensions.Deployment: // TODO(madhusudancs): Make this smarter by admitting MatchExpressions with Equals // operator, DoubleEquals operator and In operator with only one element in the set. if len(t.Spec.Selector.MatchExpressions) > 0 { return "", fmt.Errorf("couldn't convert expressions - \"%+v\" to map-based selector format", t.Spec.Selector.MatchExpressions) } return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil case *extensions.ReplicaSet: // TODO(madhusudancs): Make this smarter by admitting MatchExpressions with Equals // operator, DoubleEquals operator and In operator with only one element in the set. if len(t.Spec.Selector.MatchExpressions) > 0 { return "", fmt.Errorf("couldn't convert expressions - \"%+v\" to map-based selector format", t.Spec.Selector.MatchExpressions) } return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil default: gvk, err := api.Scheme.ObjectKind(object) if err != nil { return "", err } return "", fmt.Errorf("cannot extract pod selector from %v", gvk) } }, PortsForObject: func(object runtime.Object) ([]string, error) { // TODO: replace with a swagger schema based approach (identify pod selector via schema introspection) switch t := object.(type) { case *api.ReplicationController: return getPorts(t.Spec.Template.Spec), nil case *api.Pod: return getPorts(t.Spec), nil case *api.Service: return getServicePorts(t.Spec), nil case *extensions.Deployment: return getPorts(t.Spec.Template.Spec), nil case *extensions.ReplicaSet: return getPorts(t.Spec.Template.Spec), nil default: gvk, err := api.Scheme.ObjectKind(object) if err != nil { return nil, err } return nil, fmt.Errorf("cannot extract ports from %v", gvk) } }, LabelsForObject: func(object runtime.Object) (map[string]string, error) { return meta.NewAccessor().Labels(object) }, LogsForObject: func(object, options runtime.Object) (*restclient.Request, error) { c, err := clients.ClientForVersion(nil) if err != nil { return nil, err } switch t := object.(type) { case *api.Pod: opts, ok := options.(*api.PodLogOptions) if !ok { return nil, errors.New("provided options object is not a PodLogOptions") } return c.Pods(t.Namespace).GetLogs(t.Name, opts), nil case *api.ReplicationController: opts, ok := options.(*api.PodLogOptions) if !ok { return nil, errors.New("provided options object is not a PodLogOptions") } selector := labels.SelectorFromSet(t.Spec.Selector) sortBy := func(pods []*api.Pod) sort.Interface { return controller.ByLogging(pods) } pod, numPods, err := GetFirstPod(c, t.Namespace, selector, 20*time.Second, sortBy) if err != nil { return nil, err } if numPods > 1 { fmt.Fprintf(os.Stderr, "Found %v pods, using pod/%v\n", numPods, pod.Name) } return c.Pods(pod.Namespace).GetLogs(pod.Name, opts), nil case *extensions.ReplicaSet: opts, ok := options.(*api.PodLogOptions) if !ok { return nil, errors.New("provided options object is not a PodLogOptions") } selector, err := unversioned.LabelSelectorAsSelector(t.Spec.Selector) if err != nil { return nil, fmt.Errorf("invalid label selector: %v", err) } sortBy := func(pods []*api.Pod) sort.Interface { return controller.ByLogging(pods) } pod, numPods, err := GetFirstPod(c, t.Namespace, selector, 20*time.Second, sortBy) if err != nil { return nil, err } if numPods > 1 { fmt.Fprintf(os.Stderr, "Found %v pods, using pod/%v\n", numPods, pod.Name) } return c.Pods(pod.Namespace).GetLogs(pod.Name, opts), nil default: gvk, err := api.Scheme.ObjectKind(object) if err != nil { return nil, err } return nil, fmt.Errorf("cannot get the logs from %v", gvk) } }, PauseObject: func(object runtime.Object) (bool, error) { c, err := clients.ClientForVersion(nil) if err != nil { return false, err } switch t := object.(type) { case *extensions.Deployment: if t.Spec.Paused { return true, nil } t.Spec.Paused = true _, err := c.Extensions().Deployments(t.Namespace).Update(t) return false, err default: gvk, err := api.Scheme.ObjectKind(object) if err != nil { return false, err } return false, fmt.Errorf("cannot pause %v", gvk) } }, ResumeObject: func(object runtime.Object) (bool, error) { c, err := clients.ClientForVersion(nil) if err != nil { return false, err } switch t := object.(type) { case *extensions.Deployment: if !t.Spec.Paused { return true, nil } t.Spec.Paused = false _, err := c.Extensions().Deployments(t.Namespace).Update(t) return false, err default: gvk, err := api.Scheme.ObjectKind(object) if err != nil { return false, err } return false, fmt.Errorf("cannot resume %v", gvk) } }, Scaler: func(mapping *meta.RESTMapping) (kubectl.Scaler, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() client, err := clients.ClientForVersion(&mappingVersion) if err != nil { return nil, err } return kubectl.ScalerFor(mapping.GroupVersionKind.GroupKind(), client) }, Reaper: func(mapping *meta.RESTMapping) (kubectl.Reaper, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() client, err := clients.ClientForVersion(&mappingVersion) if err != nil { return nil, err } return kubectl.ReaperFor(mapping.GroupVersionKind.GroupKind(), client) }, HistoryViewer: func(mapping *meta.RESTMapping) (kubectl.HistoryViewer, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() client, err := clients.ClientForVersion(&mappingVersion) clientset := clientset.FromUnversionedClient(client) if err != nil { return nil, err } return kubectl.HistoryViewerFor(mapping.GroupVersionKind.GroupKind(), clientset) }, Rollbacker: func(mapping *meta.RESTMapping) (kubectl.Rollbacker, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() client, err := clients.ClientForVersion(&mappingVersion) if err != nil { return nil, err } return kubectl.RollbackerFor(mapping.GroupVersionKind.GroupKind(), client) }, Validator: func(validate bool, cacheDir string) (validation.Schema, error) { if validate { client, err := clients.ClientForVersion(nil) if err != nil { return nil, err } dir := cacheDir if len(dir) > 0 { version, err := client.ServerVersion() if err != nil { return nil, err } dir = path.Join(cacheDir, version.String()) } return &clientSwaggerSchema{ c: client, cacheDir: dir, mapper: api.RESTMapper, }, nil } return validation.NullSchema{}, nil }, SwaggerSchema: func(gvk unversioned.GroupVersionKind) (*swagger.ApiDeclaration, error) { version := gvk.GroupVersion() client, err := clients.ClientForVersion(&version) if err != nil { return nil, err } return client.Discovery().SwaggerSchema(version) }, DefaultNamespace: func() (string, bool, error) { return clientConfig.Namespace() }, Generators: func(cmdName string) map[string]kubectl.Generator { return DefaultGenerators(cmdName) }, CanBeExposed: func(kind unversioned.GroupKind) error { switch kind { case api.Kind("ReplicationController"), api.Kind("Service"), api.Kind("Pod"), extensions.Kind("Deployment"), extensions.Kind("ReplicaSet"): // nothing to do here default: return fmt.Errorf("cannot expose a %s", kind) } return nil }, CanBeAutoscaled: func(kind unversioned.GroupKind) error { switch kind { case api.Kind("ReplicationController"), extensions.Kind("Deployment"), extensions.Kind("ReplicaSet"): // nothing to do here default: return fmt.Errorf("cannot autoscale a %v", kind) } return nil }, AttachablePodForObject: func(object runtime.Object) (*api.Pod, error) { client, err := clients.ClientForVersion(nil) if err != nil { return nil, err } switch t := object.(type) { case *api.ReplicationController: selector := labels.SelectorFromSet(t.Spec.Selector) sortBy := func(pods []*api.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) } pod, _, err := GetFirstPod(client, t.Namespace, selector, 1*time.Minute, sortBy) return pod, err case *extensions.Deployment: selector, err := unversioned.LabelSelectorAsSelector(t.Spec.Selector) if err != nil { return nil, fmt.Errorf("invalid label selector: %v", err) } sortBy := func(pods []*api.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) } pod, _, err := GetFirstPod(client, t.Namespace, selector, 1*time.Minute, sortBy) return pod, err case *batch.Job: selector, err := unversioned.LabelSelectorAsSelector(t.Spec.Selector) if err != nil { return nil, fmt.Errorf("invalid label selector: %v", err) } sortBy := func(pods []*api.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) } pod, _, err := GetFirstPod(client, t.Namespace, selector, 1*time.Minute, sortBy) return pod, err case *api.Pod: return t, nil default: gvk, err := api.Scheme.ObjectKind(object) if err != nil { return nil, err } return nil, fmt.Errorf("cannot attach to %v: not implemented", gvk) } }, EditorEnvs: func() []string { return []string{"KUBE_EDITOR", "EDITOR"} }, PrintObjectSpecificMessage: func(obj runtime.Object, out io.Writer) { switch obj := obj.(type) { case *api.Service: if obj.Spec.Type == api.ServiceTypeNodePort { msg := fmt.Sprintf( `You have exposed your service on an external port on all nodes in your cluster. If you want to expose this service to the external internet, you may need to set up firewall rules for the service port(s) (%s) to serve traffic. See http://releases.k8s.io/HEAD/docs/user-guide/services-firewalls.md for more details. `, makePortsString(obj.Spec.Ports, true)) out.Write([]byte(msg)) } } }, } }
// NewFactory creates a factory with the default Kubernetes resources defined // if optionalClientConfig is nil, then flags will be bound to a new clientcmd.ClientConfig. // if optionalClientConfig is not nil, then this factory will make use of it. func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { mapper := kubectl.ShortcutExpander{RESTMapper: api.RESTMapper} flags := pflag.NewFlagSet("", pflag.ContinueOnError) flags.SetNormalizeFunc(util.WarnWordSepNormalizeFunc) // Warn for "_" flags generators := map[string]kubectl.Generator{ "run/v1": kubectl.BasicReplicationController{}, "run-pod/v1": kubectl.BasicPod{}, "service/v1": kubectl.ServiceGeneratorV1{}, "service/v2": kubectl.ServiceGeneratorV2{}, } clientConfig := optionalClientConfig if optionalClientConfig == nil { clientConfig = DefaultClientConfig(flags) } clients := NewClientCache(clientConfig) return &Factory{ clients: clients, flags: flags, generators: generators, Object: func() (meta.RESTMapper, runtime.ObjectTyper) { cfg, err := clientConfig.ClientConfig() CheckErr(err) cmdApiVersion := cfg.Version return kubectl.OutputVersionMapper{RESTMapper: mapper, OutputVersion: cmdApiVersion}, api.Scheme }, Client: func() (*client.Client, error) { return clients.ClientForVersion("") }, ClientConfig: func() (*client.Config, error) { return clients.ClientConfigForVersion("") }, RESTClient: func(mapping *meta.RESTMapping) (resource.RESTClient, error) { group, err := api.RESTMapper.GroupForResource(mapping.Resource) if err != nil { return nil, err } client, err := clients.ClientForVersion(mapping.APIVersion) if err != nil { return nil, err } switch group { case "": return client.RESTClient, nil case "extensions": return client.ExtensionsClient.RESTClient, nil } return nil, fmt.Errorf("unable to get RESTClient for resource '%s'", mapping.Resource) }, Describer: func(mapping *meta.RESTMapping) (kubectl.Describer, error) { group, err := api.RESTMapper.GroupForResource(mapping.Resource) if err != nil { return nil, err } client, err := clients.ClientForVersion(mapping.APIVersion) if err != nil { return nil, err } if describer, ok := kubectl.DescriberFor(group, mapping.Kind, client); ok { return describer, nil } return nil, fmt.Errorf("no description has been implemented for %q", mapping.Kind) }, Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll bool, columnLabels []string) (kubectl.ResourcePrinter, error) { return kubectl.NewHumanReadablePrinter(noHeaders, withNamespace, wide, showAll, columnLabels), nil }, PodSelectorForObject: func(object runtime.Object) (string, error) { // TODO: replace with a swagger schema based approach (identify pod selector via schema introspection) switch t := object.(type) { case *api.ReplicationController: return kubectl.MakeLabels(t.Spec.Selector), nil case *api.Pod: if len(t.Labels) == 0 { return "", fmt.Errorf("the pod has no labels and cannot be exposed") } return kubectl.MakeLabels(t.Labels), nil case *api.Service: if t.Spec.Selector == nil { return "", fmt.Errorf("the service has no pod selector set") } return kubectl.MakeLabels(t.Spec.Selector), nil default: _, kind, err := api.Scheme.ObjectVersionAndKind(object) if err != nil { return "", err } return "", fmt.Errorf("cannot extract pod selector from %s", kind) } }, PortsForObject: func(object runtime.Object) ([]string, error) { // TODO: replace with a swagger schema based approach (identify pod selector via schema introspection) switch t := object.(type) { case *api.ReplicationController: return getPorts(t.Spec.Template.Spec), nil case *api.Pod: return getPorts(t.Spec), nil case *api.Service: return getServicePorts(t.Spec), nil default: _, kind, err := api.Scheme.ObjectVersionAndKind(object) if err != nil { return nil, err } return nil, fmt.Errorf("cannot extract ports from %s", kind) } }, LabelsForObject: func(object runtime.Object) (map[string]string, error) { return meta.NewAccessor().Labels(object) }, LogsForObject: func(object, options runtime.Object) (*client.Request, error) { c, err := clients.ClientForVersion("") if err != nil { return nil, err } switch t := object.(type) { case *api.Pod: opts, ok := options.(*api.PodLogOptions) if !ok { return nil, errors.New("provided options object is not a PodLogOptions") } return c.PodLogs(t.Namespace).Get(t.Name, opts) default: _, kind, err := api.Scheme.ObjectVersionAndKind(object) if err != nil { return nil, err } return nil, fmt.Errorf("cannot get the logs from %s", kind) } }, Scaler: func(mapping *meta.RESTMapping) (kubectl.Scaler, error) { client, err := clients.ClientForVersion(mapping.APIVersion) if err != nil { return nil, err } return kubectl.ScalerFor(mapping.Kind, client) }, Reaper: func(mapping *meta.RESTMapping) (kubectl.Reaper, error) { client, err := clients.ClientForVersion(mapping.APIVersion) if err != nil { return nil, err } return kubectl.ReaperFor(mapping.Kind, client) }, Validator: func(validate bool, cacheDir string) (validation.Schema, error) { if validate { client, err := clients.ClientForVersion("") if err != nil { return nil, err } dir := cacheDir if len(dir) > 0 { version, err := client.ServerVersion() if err != nil { return nil, err } dir = path.Join(cacheDir, version.String()) } return &clientSwaggerSchema{ c: client, cacheDir: dir, mapper: api.RESTMapper, }, nil } return validation.NullSchema{}, nil }, DefaultNamespace: func() (string, bool, error) { return clientConfig.Namespace() }, Generator: func(name string) (kubectl.Generator, bool) { generator, ok := generators[name] return generator, ok }, CanBeExposed: func(kind string) error { if kind != "ReplicationController" && kind != "Service" && kind != "Pod" { return fmt.Errorf("invalid resource provided: %v, only a replication controller, service or pod is accepted", kind) } return nil }, } }
func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *ExposeOptions) error { namespace, enforceNamespace, err := f.DefaultNamespace() if err != nil { return err } mapper, typer := f.Object() r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). ContinueOnError(). NamespaceParam(namespace).DefaultNamespace(). FilenameParam(enforceNamespace, options.Filenames...). ResourceTypeOrNameArgs(false, args...). Flatten(). Do() infos, err := r.Infos() if err != nil { return err } if len(infos) > 1 { return fmt.Errorf("multiple resources provided: %v", args) } info := infos[0] mapping := info.ResourceMapping() if err := f.CanBeExposed(mapping.Kind); err != nil { return err } // Get the input object inputObject, err := r.Object() if err != nil { return err } // Get the generator, setup and validate all required parameters generatorName := cmdutil.GetFlagString(cmd, "generator") generator, found := f.Generator(generatorName) if !found { return cmdutil.UsageError(cmd, fmt.Sprintf("generator %q not found.", generatorName)) } names := generator.ParamNames() params := kubectl.MakeParams(cmd, names) name := info.Name if len(name) > validation.DNS952LabelMaxLength { name = name[:validation.DNS952LabelMaxLength] } params["default-name"] = name // For objects that need a pod selector, derive it from the exposed object in case a user // didn't explicitly specify one via --selector if s, found := params["selector"]; found && kubectl.IsZero(s) { s, err := f.PodSelectorForObject(inputObject) if err != nil { return cmdutil.UsageError(cmd, fmt.Sprintf("couldn't find selectors via --selector flag or introspection: %s", err)) } params["selector"] = s } if cmdutil.GetFlagInt(cmd, "port") < 1 { noPorts := true for _, param := range names { if param.Name == "port" { noPorts = false break } } if cmdutil.GetFlagInt(cmd, "port") < 0 && !noPorts { ports, err := f.PortsForObject(inputObject) if err != nil { return cmdutil.UsageError(cmd, fmt.Sprintf("couldn't find port via --port flag or introspection: %s", err)) } switch len(ports) { case 0: return cmdutil.UsageError(cmd, "couldn't find port via --port flag or introspection") case 1: params["port"] = ports[0] default: return cmdutil.UsageError(cmd, fmt.Sprintf("multiple ports to choose from: %v, please explicitly specify a port using the --port flag.", ports)) } } } if cmdutil.GetFlagBool(cmd, "create-external-load-balancer") { params["create-external-load-balancer"] = "true" } if kubectl.IsZero(params["labels"]) { labels, err := f.LabelsForObject(inputObject) if err != nil { return err } params["labels"] = kubectl.MakeLabels(labels) } if v := cmdutil.GetFlagString(cmd, "type"); v != "" { params["type"] = v } err = kubectl.ValidateParams(names, params) if err != nil { return err } // Expose new object object, err := generator.Generate(params) if err != nil { return err } inline := cmdutil.GetFlagString(cmd, "overrides") if len(inline) > 0 { object, err = cmdutil.Merge(object, inline, mapping.Kind) if err != nil { return err } } resourceMapper := &resource.Mapper{ObjectTyper: typer, RESTMapper: mapper, ClientMapper: f.ClientMapperForCommand()} info, err = resourceMapper.InfoForObject(object) if err != nil { return err } // TODO: extract this flag to a central location, when such a location exists. if cmdutil.GetFlagBool(cmd, "dry-run") { fmt.Fprintln(out, "running in dry-run mode...") } else { data, err := info.Mapping.Codec.Encode(object) if err != nil { return err } object, err = resource.NewHelper(info.Client, info.Mapping).Create(namespace, false, data) if err != nil { return err } } outputFormat := cmdutil.GetFlagString(cmd, "output") if outputFormat != "" { return f.PrintObject(cmd, object, out) } cmdutil.PrintSuccess(mapper, false, out, info.Mapping.Resource, info.Name, "exposed") return nil }
// NewFactory creates a factory with the default Kubernetes resources defined // if optionalClientConfig is nil, then flags will be bound to a new clientcmd.ClientConfig. // if optionalClientConfig is not nil, then this factory will make use of it. func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { flags := pflag.NewFlagSet("", pflag.ContinueOnError) flags.SetNormalizeFunc(utilflag.WarnWordSepNormalizeFunc) // Warn for "_" flags clientConfig := optionalClientConfig if optionalClientConfig == nil { clientConfig = DefaultClientConfig(flags) } clients := NewClientCache(clientConfig) return &Factory{ clients: clients, flags: flags, Object: func() (meta.RESTMapper, runtime.ObjectTyper) { cfg, err := clientConfig.ClientConfig() checkErrWithPrefix("failed to get client config: ", err) cmdApiVersion := unversioned.GroupVersion{} if cfg.GroupVersion != nil { cmdApiVersion = *cfg.GroupVersion } mapper := registered.RESTMapper() discoveryClient, err := discovery.NewDiscoveryClientForConfig(cfg) // if we can find the server version and it's current enough to have discovery information, use it. Otherwise, // fallback to our hardcoded list if err == nil { if serverVersion, err := discoveryClient.ServerVersion(); err == nil && useDiscoveryRESTMapper(serverVersion.GitVersion) { // register third party resources with the api machinery groups. This probably should be done, but // its consistent with old code, so we'll start with it. if err := registerThirdPartyResources(discoveryClient); err != nil { fmt.Fprintf(os.Stderr, "Unable to register third party resources: %v\n", err) } // ThirdPartyResourceData is special. It's not discoverable, but needed for thirdparty resource listing // TODO eliminate this once we're truly generic. thirdPartyResourceDataMapper := meta.NewDefaultRESTMapper([]unversioned.GroupVersion{extensionsv1beta1.SchemeGroupVersion}, registered.InterfacesFor) thirdPartyResourceDataMapper.Add(extensionsv1beta1.SchemeGroupVersion.WithKind("ThirdPartyResourceData"), meta.RESTScopeNamespace) mapper = meta.FirstHitRESTMapper{ MultiRESTMapper: meta.MultiRESTMapper{ discovery.NewDeferredDiscoveryRESTMapper(discoveryClient, registered.InterfacesFor), thirdPartyResourceDataMapper, }, } } } // wrap with shortcuts mapper = NewShortcutExpander(mapper, discoveryClient) // wrap with output preferences mapper = kubectl.OutputVersionMapper{RESTMapper: mapper, OutputVersions: []unversioned.GroupVersion{cmdApiVersion}} return mapper, api.Scheme }, UnstructuredObject: func() (meta.RESTMapper, runtime.ObjectTyper, error) { cfg, err := clients.ClientConfigForVersion(nil) if err != nil { return nil, nil, err } dc, err := discovery.NewDiscoveryClientForConfig(cfg) if err != nil { return nil, nil, err } groupResources, err := discovery.GetAPIGroupResources(dc) if err != nil { return nil, nil, err } // Register unknown APIs as third party for now to make // validation happy. TODO perhaps make a dynamic schema // validator to avoid this. for _, group := range groupResources { for _, version := range group.Group.Versions { gv := unversioned.GroupVersion{Group: group.Group.Name, Version: version.Version} if !registered.IsRegisteredVersion(gv) { registered.AddThirdPartyAPIGroupVersions(gv) } } } mapper := discovery.NewRESTMapper(groupResources, meta.InterfacesForUnstructured) typer := discovery.NewUnstructuredObjectTyper(groupResources) return NewShortcutExpander(mapper, dc), typer, nil }, RESTClient: func() (*restclient.RESTClient, error) { clientConfig, err := clients.ClientConfigForVersion(nil) if err != nil { return nil, err } return restclient.RESTClientFor(clientConfig) }, ClientSet: func() (*internalclientset.Clientset, error) { return clients.ClientSetForVersion(nil) }, ClientConfig: func() (*restclient.Config, error) { return clients.ClientConfigForVersion(nil) }, ClientForMapping: func(mapping *meta.RESTMapping) (resource.RESTClient, error) { cfg, err := clientConfig.ClientConfig() if err != nil { return nil, err } if err := client.SetKubernetesDefaults(cfg); err != nil { return nil, err } gvk := mapping.GroupVersionKind switch gvk.Group { case federation.GroupName: mappingVersion := mapping.GroupVersionKind.GroupVersion() return clients.FederationClientForVersion(&mappingVersion) case api.GroupName: cfg.APIPath = "/api" default: cfg.APIPath = "/apis" } gv := gvk.GroupVersion() cfg.GroupVersion = &gv if registered.IsThirdPartyAPIGroupVersion(gvk.GroupVersion()) { cfg.NegotiatedSerializer = thirdpartyresourcedata.NewNegotiatedSerializer(api.Codecs, gvk.Kind, gv, gv) } return restclient.RESTClientFor(cfg) }, UnstructuredClientForMapping: func(mapping *meta.RESTMapping) (resource.RESTClient, error) { cfg, err := clientConfig.ClientConfig() if err != nil { return nil, err } if err := restclient.SetKubernetesDefaults(cfg); err != nil { return nil, err } cfg.APIPath = "/apis" if mapping.GroupVersionKind.Group == api.GroupName { cfg.APIPath = "/api" } gv := mapping.GroupVersionKind.GroupVersion() cfg.ContentConfig = dynamic.ContentConfig() cfg.GroupVersion = &gv return restclient.RESTClientFor(cfg) }, Describer: func(mapping *meta.RESTMapping) (kubectl.Describer, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() if mapping.GroupVersionKind.Group == federation.GroupName { fedClientSet, err := clients.FederationClientSetForVersion(&mappingVersion) if err != nil { return nil, err } if mapping.GroupVersionKind.Kind == "Cluster" { return &kubectl.ClusterDescriber{Interface: fedClientSet}, nil } } clientset, err := clients.ClientSetForVersion(&mappingVersion) if err != nil { return nil, err } if describer, ok := kubectl.DescriberFor(mapping.GroupVersionKind.GroupKind(), clientset); ok { return describer, nil } return nil, fmt.Errorf("no description has been implemented for %q", mapping.GroupVersionKind.Kind) }, Decoder: func(toInternal bool) runtime.Decoder { var decoder runtime.Decoder if toInternal { decoder = api.Codecs.UniversalDecoder() } else { decoder = api.Codecs.UniversalDeserializer() } return thirdpartyresourcedata.NewDecoder(decoder, "") }, JSONEncoder: func() runtime.Encoder { return api.Codecs.LegacyCodec(registered.EnabledVersions()...) }, Printer: func(mapping *meta.RESTMapping, options kubectl.PrintOptions) (kubectl.ResourcePrinter, error) { return kubectl.NewHumanReadablePrinter(options), nil }, MapBasedSelectorForObject: func(object runtime.Object) (string, error) { // TODO: replace with a swagger schema based approach (identify pod selector via schema introspection) switch t := object.(type) { case *api.ReplicationController: return kubectl.MakeLabels(t.Spec.Selector), nil case *api.Pod: if len(t.Labels) == 0 { return "", fmt.Errorf("the pod has no labels and cannot be exposed") } return kubectl.MakeLabels(t.Labels), nil case *api.Service: if t.Spec.Selector == nil { return "", fmt.Errorf("the service has no pod selector set") } return kubectl.MakeLabels(t.Spec.Selector), nil case *extensions.Deployment: // TODO(madhusudancs): Make this smarter by admitting MatchExpressions with Equals // operator, DoubleEquals operator and In operator with only one element in the set. if len(t.Spec.Selector.MatchExpressions) > 0 { return "", fmt.Errorf("couldn't convert expressions - \"%+v\" to map-based selector format", t.Spec.Selector.MatchExpressions) } return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil case *extensions.ReplicaSet: // TODO(madhusudancs): Make this smarter by admitting MatchExpressions with Equals // operator, DoubleEquals operator and In operator with only one element in the set. if len(t.Spec.Selector.MatchExpressions) > 0 { return "", fmt.Errorf("couldn't convert expressions - \"%+v\" to map-based selector format", t.Spec.Selector.MatchExpressions) } return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil default: gvks, _, err := api.Scheme.ObjectKinds(object) if err != nil { return "", err } return "", fmt.Errorf("cannot extract pod selector from %v", gvks[0]) } }, PortsForObject: func(object runtime.Object) ([]string, error) { // TODO: replace with a swagger schema based approach (identify pod selector via schema introspection) switch t := object.(type) { case *api.ReplicationController: return getPorts(t.Spec.Template.Spec), nil case *api.Pod: return getPorts(t.Spec), nil case *api.Service: return getServicePorts(t.Spec), nil case *extensions.Deployment: return getPorts(t.Spec.Template.Spec), nil case *extensions.ReplicaSet: return getPorts(t.Spec.Template.Spec), nil default: gvks, _, err := api.Scheme.ObjectKinds(object) if err != nil { return nil, err } return nil, fmt.Errorf("cannot extract ports from %v", gvks[0]) } }, ProtocolsForObject: func(object runtime.Object) (map[string]string, error) { // TODO: replace with a swagger schema based approach (identify pod selector via schema introspection) switch t := object.(type) { case *api.ReplicationController: return getProtocols(t.Spec.Template.Spec), nil case *api.Pod: return getProtocols(t.Spec), nil case *api.Service: return getServiceProtocols(t.Spec), nil case *extensions.Deployment: return getProtocols(t.Spec.Template.Spec), nil case *extensions.ReplicaSet: return getProtocols(t.Spec.Template.Spec), nil default: gvks, _, err := api.Scheme.ObjectKinds(object) if err != nil { return nil, err } return nil, fmt.Errorf("cannot extract protocols from %v", gvks[0]) } }, LabelsForObject: func(object runtime.Object) (map[string]string, error) { return meta.NewAccessor().Labels(object) }, LogsForObject: func(object, options runtime.Object) (*restclient.Request, error) { clientset, err := clients.ClientSetForVersion(nil) if err != nil { return nil, err } switch t := object.(type) { case *api.Pod: opts, ok := options.(*api.PodLogOptions) if !ok { return nil, errors.New("provided options object is not a PodLogOptions") } return clientset.Core().Pods(t.Namespace).GetLogs(t.Name, opts), nil case *api.ReplicationController: opts, ok := options.(*api.PodLogOptions) if !ok { return nil, errors.New("provided options object is not a PodLogOptions") } selector := labels.SelectorFromSet(t.Spec.Selector) sortBy := func(pods []*api.Pod) sort.Interface { return controller.ByLogging(pods) } pod, numPods, err := GetFirstPod(clientset.Core(), t.Namespace, selector, 20*time.Second, sortBy) if err != nil { return nil, err } if numPods > 1 { fmt.Fprintf(os.Stderr, "Found %v pods, using pod/%v\n", numPods, pod.Name) } return clientset.Core().Pods(pod.Namespace).GetLogs(pod.Name, opts), nil case *extensions.ReplicaSet: opts, ok := options.(*api.PodLogOptions) if !ok { return nil, errors.New("provided options object is not a PodLogOptions") } selector, err := unversioned.LabelSelectorAsSelector(t.Spec.Selector) if err != nil { return nil, fmt.Errorf("invalid label selector: %v", err) } sortBy := func(pods []*api.Pod) sort.Interface { return controller.ByLogging(pods) } pod, numPods, err := GetFirstPod(clientset.Core(), t.Namespace, selector, 20*time.Second, sortBy) if err != nil { return nil, err } if numPods > 1 { fmt.Fprintf(os.Stderr, "Found %v pods, using pod/%v\n", numPods, pod.Name) } return clientset.Core().Pods(pod.Namespace).GetLogs(pod.Name, opts), nil default: gvks, _, err := api.Scheme.ObjectKinds(object) if err != nil { return nil, err } return nil, fmt.Errorf("cannot get the logs from %v", gvks[0]) } }, PauseObject: func(object runtime.Object) (bool, error) { clientset, err := clients.ClientSetForVersion(nil) if err != nil { return false, err } switch t := object.(type) { case *extensions.Deployment: if t.Spec.Paused { return true, nil } t.Spec.Paused = true _, err := clientset.Extensions().Deployments(t.Namespace).Update(t) return false, err default: gvks, _, err := api.Scheme.ObjectKinds(object) if err != nil { return false, err } return false, fmt.Errorf("cannot pause %v", gvks[0]) } }, ResumeObject: func(object runtime.Object) (bool, error) { clientset, err := clients.ClientSetForVersion(nil) if err != nil { return false, err } switch t := object.(type) { case *extensions.Deployment: if !t.Spec.Paused { return true, nil } t.Spec.Paused = false _, err := clientset.Extensions().Deployments(t.Namespace).Update(t) return false, err default: gvks, _, err := api.Scheme.ObjectKinds(object) if err != nil { return false, err } return false, fmt.Errorf("cannot resume %v", gvks[0]) } }, Scaler: func(mapping *meta.RESTMapping) (kubectl.Scaler, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() clientset, err := clients.ClientSetForVersion(&mappingVersion) if err != nil { return nil, err } return kubectl.ScalerFor(mapping.GroupVersionKind.GroupKind(), clientset) }, Reaper: func(mapping *meta.RESTMapping) (kubectl.Reaper, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() clientset, err := clients.ClientSetForVersion(&mappingVersion) if err != nil { return nil, err } return kubectl.ReaperFor(mapping.GroupVersionKind.GroupKind(), clientset) }, HistoryViewer: func(mapping *meta.RESTMapping) (kubectl.HistoryViewer, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() clientset, err := clients.ClientSetForVersion(&mappingVersion) if err != nil { return nil, err } return kubectl.HistoryViewerFor(mapping.GroupVersionKind.GroupKind(), clientset) }, Rollbacker: func(mapping *meta.RESTMapping) (kubectl.Rollbacker, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() clientset, err := clients.ClientSetForVersion(&mappingVersion) if err != nil { return nil, err } return kubectl.RollbackerFor(mapping.GroupVersionKind.GroupKind(), clientset) }, StatusViewer: func(mapping *meta.RESTMapping) (kubectl.StatusViewer, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() clientset, err := clients.ClientSetForVersion(&mappingVersion) if err != nil { return nil, err } return kubectl.StatusViewerFor(mapping.GroupVersionKind.GroupKind(), clientset) }, Validator: func(validate bool, cacheDir string) (validation.Schema, error) { if validate { clientConfig, err := clients.ClientConfigForVersion(nil) if err != nil { return nil, err } restclient, err := restclient.RESTClientFor(clientConfig) if err != nil { return nil, err } clientset, err := clients.ClientSetForVersion(nil) if err != nil { return nil, err } dir := cacheDir if len(dir) > 0 { version, err := clientset.Discovery().ServerVersion() if err != nil { return nil, err } dir = path.Join(cacheDir, version.String()) } fedClient, err := clients.FederationClientForVersion(nil) if err != nil { return nil, err } return &clientSwaggerSchema{ c: restclient, fedc: fedClient, cacheDir: dir, }, nil } return validation.NullSchema{}, nil }, SwaggerSchema: func(gvk unversioned.GroupVersionKind) (*swagger.ApiDeclaration, error) { version := gvk.GroupVersion() clientset, err := clients.ClientSetForVersion(&version) if err != nil { return nil, err } return clientset.Discovery().SwaggerSchema(version) }, DefaultNamespace: func() (string, bool, error) { return clientConfig.Namespace() }, Generators: func(cmdName string) map[string]kubectl.Generator { return DefaultGenerators(cmdName) }, CanBeExposed: func(kind unversioned.GroupKind) error { switch kind { case api.Kind("ReplicationController"), api.Kind("Service"), api.Kind("Pod"), extensions.Kind("Deployment"), extensions.Kind("ReplicaSet"): // nothing to do here default: return fmt.Errorf("cannot expose a %s", kind) } return nil }, CanBeAutoscaled: func(kind unversioned.GroupKind) error { switch kind { case api.Kind("ReplicationController"), extensions.Kind("Deployment"), extensions.Kind("ReplicaSet"): // nothing to do here default: return fmt.Errorf("cannot autoscale a %v", kind) } return nil }, AttachablePodForObject: func(object runtime.Object) (*api.Pod, error) { clientset, err := clients.ClientSetForVersion(nil) if err != nil { return nil, err } switch t := object.(type) { case *api.ReplicationController: selector := labels.SelectorFromSet(t.Spec.Selector) sortBy := func(pods []*api.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) } pod, _, err := GetFirstPod(clientset.Core(), t.Namespace, selector, 1*time.Minute, sortBy) return pod, err case *extensions.Deployment: selector, err := unversioned.LabelSelectorAsSelector(t.Spec.Selector) if err != nil { return nil, fmt.Errorf("invalid label selector: %v", err) } sortBy := func(pods []*api.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) } pod, _, err := GetFirstPod(clientset.Core(), t.Namespace, selector, 1*time.Minute, sortBy) return pod, err case *batch.Job: selector, err := unversioned.LabelSelectorAsSelector(t.Spec.Selector) if err != nil { return nil, fmt.Errorf("invalid label selector: %v", err) } sortBy := func(pods []*api.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) } pod, _, err := GetFirstPod(clientset.Core(), t.Namespace, selector, 1*time.Minute, sortBy) return pod, err case *api.Pod: return t, nil default: gvks, _, err := api.Scheme.ObjectKinds(object) if err != nil { return nil, err } return nil, fmt.Errorf("cannot attach to %v: not implemented", gvks[0]) } }, // UpdatePodSpecForObject update the pod specification for the provided object UpdatePodSpecForObject: func(obj runtime.Object, fn func(*api.PodSpec) error) (bool, error) { // TODO: replace with a swagger schema based approach (identify pod template via schema introspection) switch t := obj.(type) { case *api.Pod: return true, fn(&t.Spec) case *api.ReplicationController: if t.Spec.Template == nil { t.Spec.Template = &api.PodTemplateSpec{} } return true, fn(&t.Spec.Template.Spec) case *extensions.Deployment: return true, fn(&t.Spec.Template.Spec) case *extensions.DaemonSet: return true, fn(&t.Spec.Template.Spec) case *extensions.ReplicaSet: return true, fn(&t.Spec.Template.Spec) case *apps.PetSet: return true, fn(&t.Spec.Template.Spec) case *batch.Job: return true, fn(&t.Spec.Template.Spec) default: return false, fmt.Errorf("the object is not a pod or does not have a pod template") } }, EditorEnvs: func() []string { return []string{"KUBE_EDITOR", "EDITOR"} }, PrintObjectSpecificMessage: func(obj runtime.Object, out io.Writer) { switch obj := obj.(type) { case *api.Service: if obj.Spec.Type == api.ServiceTypeNodePort { msg := fmt.Sprintf( `You have exposed your service on an external port on all nodes in your cluster. If you want to expose this service to the external internet, you may need to set up firewall rules for the service port(s) (%s) to serve traffic. See http://releases.k8s.io/HEAD/docs/user-guide/services-firewalls.md for more details. `, makePortsString(obj.Spec.Ports, true)) out.Write([]byte(msg)) } if _, ok := obj.Annotations[service.AnnotationLoadBalancerSourceRangesKey]; ok { msg := fmt.Sprintf( `You are using service annotation [service.beta.kubernetes.io/load-balancer-source-ranges]. It has been promoted to field [loadBalancerSourceRanges] in service spec. This annotation will be deprecated in the future. Please use the loadBalancerSourceRanges field instead. See http://releases.k8s.io/HEAD/docs/user-guide/services-firewalls.md for more details. `) out.Write([]byte(msg)) } } }, } }
func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *ExposeOptions) error { namespace, enforceNamespace, err := f.DefaultNamespace() if err != nil { return err } mapper, typer := f.Object() r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). ContinueOnError(). NamespaceParam(namespace).DefaultNamespace(). FilenameParam(enforceNamespace, options.Filenames...). ResourceTypeOrNameArgs(false, args...). Flatten(). Do() infos, err := r.Infos() if err != nil { return err } if len(infos) > 1 { return fmt.Errorf("multiple resources provided: %v", args) } info := infos[0] mapping := info.ResourceMapping() if err := f.CanBeExposed(mapping.Kind); err != nil { return err } // Get the input object inputObject, err := r.Object() if err != nil { return err } // Get the generator, setup and validate all required parameters generatorName := cmdutil.GetFlagString(cmd, "generator") generator, found := f.Generator(generatorName) if !found { return cmdutil.UsageError(cmd, fmt.Sprintf("generator %q not found.", generatorName)) } names := generator.ParamNames() params := kubectl.MakeParams(cmd, names) name := info.Name if len(name) > validation.DNS952LabelMaxLength { name = name[:validation.DNS952LabelMaxLength] } params["default-name"] = name // For objects that need a pod selector, derive it from the exposed object in case a user // didn't explicitly specify one via --selector if s, found := params["selector"]; found && kubectl.IsZero(s) { s, err := f.PodSelectorForObject(inputObject) if err != nil { return cmdutil.UsageError(cmd, fmt.Sprintf("couldn't find selectors via --selector flag or introspection: %s", err)) } params["selector"] = s } // For objects that need a port, derive it from the exposed object in case a user // didn't explicitly specify one via --port if port, found := params["port"]; found && kubectl.IsZero(port) { ports, err := f.PortsForObject(inputObject) if err != nil { return cmdutil.UsageError(cmd, fmt.Sprintf("couldn't find port via --port flag or introspection: %s", err)) } switch len(ports) { case 0: return cmdutil.UsageError(cmd, "couldn't find port via --port flag or introspection") case 1: params["port"] = ports[0] default: params["ports"] = strings.Join(ports, ",") } } if kubectl.IsZero(params["labels"]) { labels, err := f.LabelsForObject(inputObject) if err != nil { return err } params["labels"] = kubectl.MakeLabels(labels) } if err = kubectl.ValidateParams(names, params); err != nil { return err } // Generate new object object, err := generator.Generate(params) if err != nil { return err } if inline := cmdutil.GetFlagString(cmd, "overrides"); len(inline) > 0 { object, err = cmdutil.Merge(object, inline, mapping.Kind) if err != nil { return err } } resourceMapper := &resource.Mapper{ObjectTyper: typer, RESTMapper: mapper, ClientMapper: f.ClientMapperForCommand()} info, err = resourceMapper.InfoForObject(object) if err != nil { return err } // TODO: extract this flag to a central location, when such a location exists. if cmdutil.GetFlagBool(cmd, "dry-run") { return f.PrintObject(cmd, object, out) } // Serialize the configuration into an annotation. if err := kubectl.UpdateApplyAnnotation(info); err != nil { return err } // Serialize the object with the annotation applied. object, err = resource.NewHelper(info.Client, info.Mapping).Create(namespace, false, object) if err != nil { return err } if len(cmdutil.GetFlagString(cmd, "output")) > 0 { return f.PrintObject(cmd, object, out) } cmdutil.PrintSuccess(mapper, false, out, info.Mapping.Resource, info.Name, "exposed") return nil }
// NewFactory creates a factory with the default Kubernetes resources defined // if optionalClientConfig is nil, then flags will be bound to a new clientcmd.ClientConfig. // if optionalClientConfig is not nil, then this factory will make use of it. func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { mapper := kubectl.ShortcutExpander{RESTMapper: api.RESTMapper} flags := pflag.NewFlagSet("", pflag.ContinueOnError) flags.SetNormalizeFunc(util.WarnWordSepNormalizeFunc) // Warn for "_" flags generators := map[string]kubectl.Generator{ "run/v1": kubectl.BasicReplicationController{}, "run-pod/v1": kubectl.BasicPod{}, "service/v1": kubectl.ServiceGeneratorV1{}, "service/v2": kubectl.ServiceGeneratorV2{}, } clientConfig := optionalClientConfig if optionalClientConfig == nil { clientConfig = DefaultClientConfig(flags) } clients := NewClientCache(clientConfig) expClients := NewExperimentalClientCache(clientConfig) noClientErr := errors.New("could not get client") getBothClients := func(group string, version string) (*client.Client, *client.ExperimentalClient, error) { switch group { case "api": client, err := clients.ClientForVersion(version) return client, nil, err case "experimental": client, err := clients.ClientForVersion(version) if err != nil { return nil, nil, err } expClient, err := expClients.Client() if err != nil { return nil, nil, err } return client, expClient, err } return nil, nil, noClientErr } return &Factory{ clients: clients, flags: flags, generators: generators, Object: func() (meta.RESTMapper, runtime.ObjectTyper) { cfg, err := clientConfig.ClientConfig() CheckErr(err) cmdApiVersion := cfg.Version return kubectl.OutputVersionMapper{RESTMapper: mapper, OutputVersion: cmdApiVersion}, api.Scheme }, Client: func() (*client.Client, error) { return clients.ClientForVersion("") }, ExperimentalClient: func() (*client.ExperimentalClient, error) { return expClients.Client() }, ClientConfig: func() (*client.Config, error) { return clients.ClientConfigForVersion("") }, RESTClient: func(mapping *meta.RESTMapping) (resource.RESTClient, error) { group, err := api.RESTMapper.GroupForResource(mapping.Resource) if err != nil { return nil, err } switch group { case "api": client, err := clients.ClientForVersion(mapping.APIVersion) if err != nil { return nil, err } return client.RESTClient, nil case "experimental": client, err := expClients.Client() if err != nil { return nil, err } return client.RESTClient, nil } return nil, fmt.Errorf("unable to get RESTClient for resource '%s'", mapping.Resource) }, Describer: func(mapping *meta.RESTMapping) (kubectl.Describer, error) { group, err := api.RESTMapper.GroupForResource(mapping.Resource) if err != nil { return nil, err } client, expClient, err := getBothClients(group, mapping.APIVersion) if err != nil { return nil, err } if describer, ok := kubectl.DescriberFor(mapping.Kind, client, expClient); ok { return describer, nil } return nil, fmt.Errorf("no description has been implemented for %q", mapping.Kind) }, Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll bool, columnLabels []string) (kubectl.ResourcePrinter, error) { return kubectl.NewHumanReadablePrinter(noHeaders, withNamespace, wide, showAll, columnLabels), nil }, PodSelectorForObject: func(object runtime.Object) (string, error) { // TODO: replace with a swagger schema based approach (identify pod selector via schema introspection) switch t := object.(type) { case *api.ReplicationController: return kubectl.MakeLabels(t.Spec.Selector), nil case *api.Pod: if len(t.Labels) == 0 { return "", fmt.Errorf("the pod has no labels and cannot be exposed") } return kubectl.MakeLabels(t.Labels), nil case *api.Service: if t.Spec.Selector == nil { return "", fmt.Errorf("the service has no pod selector set") } return kubectl.MakeLabels(t.Spec.Selector), nil default: _, kind, err := api.Scheme.ObjectVersionAndKind(object) if err != nil { return "", err } return "", fmt.Errorf("cannot extract pod selector from %s", kind) } }, PortsForObject: func(object runtime.Object) ([]string, error) { // TODO: replace with a swagger schema based approach (identify pod selector via schema introspection) switch t := object.(type) { case *api.ReplicationController: return getPorts(t.Spec.Template.Spec), nil case *api.Pod: return getPorts(t.Spec), nil case *api.Service: return getServicePorts(t.Spec), nil default: _, kind, err := api.Scheme.ObjectVersionAndKind(object) if err != nil { return nil, err } return nil, fmt.Errorf("cannot extract ports from %s", kind) } }, LabelsForObject: func(object runtime.Object) (map[string]string, error) { return meta.NewAccessor().Labels(object) }, Scaler: func(mapping *meta.RESTMapping) (kubectl.Scaler, error) { group, err := api.RESTMapper.GroupForResource(mapping.Resource) if err != nil { return nil, err } client, _, err := getBothClients(group, mapping.APIVersion) if err != nil { return nil, err } return kubectl.ScalerFor(mapping.Kind, kubectl.NewScalerClient(client)) }, Reaper: func(mapping *meta.RESTMapping) (kubectl.Reaper, error) { group, err := api.RESTMapper.GroupForResource(mapping.Resource) if err != nil { return nil, err } client, expClient, err := getBothClients(group, mapping.APIVersion) if err != nil { return nil, err } return kubectl.ReaperFor(mapping.Kind, client, expClient) }, Validator: func(validate bool) (validation.Schema, error) { if validate { client, err := clients.ClientForVersion("") if err != nil { return nil, err } expClient, _ := expClients.Client() return &clientSwaggerSchema{client, expClient, api.Scheme}, nil } return validation.NullSchema{}, nil }, DefaultNamespace: func() (string, bool, error) { return clientConfig.Namespace() }, Generator: func(name string) (kubectl.Generator, bool) { generator, ok := generators[name] return generator, ok }, } }
// NewFactory creates an object that holds common methods across all OpenShift commands func NewFactory(clientConfig kclientcmd.ClientConfig) *Factory { restMapper := registered.RESTMapper() clients := &clientCache{ clients: make(map[string]*client.Client), configs: make(map[string]*restclient.Config), loader: clientConfig, } w := &Factory{ Factory: cmdutil.NewFactory(clientConfig), OpenShiftClientConfig: clientConfig, clients: clients, } w.Object = func(bool) (meta.RESTMapper, runtime.ObjectTyper) { defaultMapper := ShortcutExpander{RESTMapper: kubectl.ShortcutExpander{RESTMapper: restMapper}} defaultTyper := api.Scheme // Output using whatever version was negotiated in the client cache. The // version we decode with may not be the same as what the server requires. cfg, err := clients.ClientConfigForVersion(nil) if err != nil { return defaultMapper, defaultTyper } cmdApiVersion := unversioned.GroupVersion{} if cfg.GroupVersion != nil { cmdApiVersion = *cfg.GroupVersion } // at this point we've negotiated and can get the client oclient, err := clients.ClientForVersion(nil) if err != nil { return defaultMapper, defaultTyper } cacheDir := computeDiscoverCacheDir(filepath.Join(homedir.HomeDir(), ".kube"), cfg.Host) cachedDiscoverClient := NewCachedDiscoveryClient(client.NewDiscoveryClient(oclient.RESTClient), cacheDir, time.Duration(10*time.Minute)) mapper := restmapper.NewDiscoveryRESTMapper(cachedDiscoverClient) mapper = NewShortcutExpander(cachedDiscoverClient, kubectl.ShortcutExpander{RESTMapper: mapper}) return kubectl.OutputVersionMapper{RESTMapper: mapper, OutputVersions: []unversioned.GroupVersion{cmdApiVersion}}, api.Scheme } kClientForMapping := w.Factory.ClientForMapping w.ClientForMapping = func(mapping *meta.RESTMapping) (resource.RESTClient, error) { if latest.OriginKind(mapping.GroupVersionKind) { mappingVersion := mapping.GroupVersionKind.GroupVersion() client, err := clients.ClientForVersion(&mappingVersion) if err != nil { return nil, err } return client.RESTClient, nil } return kClientForMapping(mapping) } // Save original Describer function kDescriberFunc := w.Factory.Describer w.Describer = func(mapping *meta.RESTMapping) (kubectl.Describer, error) { if latest.OriginKind(mapping.GroupVersionKind) { oClient, kClient, err := w.Clients() if err != nil { return nil, fmt.Errorf("unable to create client %s: %v", mapping.GroupVersionKind.Kind, err) } mappingVersion := mapping.GroupVersionKind.GroupVersion() cfg, err := clients.ClientConfigForVersion(&mappingVersion) if err != nil { return nil, fmt.Errorf("unable to load a client %s: %v", mapping.GroupVersionKind.Kind, err) } describer, ok := describe.DescriberFor(mapping.GroupVersionKind.GroupKind(), oClient, kClient, cfg.Host) if !ok { return nil, fmt.Errorf("no description has been implemented for %q", mapping.GroupVersionKind.Kind) } return describer, nil } return kDescriberFunc(mapping) } kScalerFunc := w.Factory.Scaler w.Scaler = func(mapping *meta.RESTMapping) (kubectl.Scaler, error) { oc, kc, err := w.Clients() if err != nil { return nil, err } if mapping.GroupVersionKind.GroupKind() == deployapi.Kind("DeploymentConfig") { return deployscaler.NewDeploymentConfigScaler(oc, kc), nil } return kScalerFunc(mapping) } kReaperFunc := w.Factory.Reaper w.Reaper = func(mapping *meta.RESTMapping) (kubectl.Reaper, error) { oc, kc, err := w.Clients() if err != nil { return nil, err } switch mapping.GroupVersionKind.GroupKind() { case deployapi.Kind("DeploymentConfig"): return deployreaper.NewDeploymentConfigReaper(oc, kc), nil case authorizationapi.Kind("Role"): return authorizationreaper.NewRoleReaper(oc, oc), nil case authorizationapi.Kind("ClusterRole"): return authorizationreaper.NewClusterRoleReaper(oc, oc, oc), nil case userapi.Kind("User"): return authenticationreaper.NewUserReaper( client.UsersInterface(oc), client.GroupsInterface(oc), client.ClusterRoleBindingsInterface(oc), client.RoleBindingsNamespacer(oc), kclient.SecurityContextConstraintsInterface(kc), ), nil case userapi.Kind("Group"): return authenticationreaper.NewGroupReaper( client.GroupsInterface(oc), client.ClusterRoleBindingsInterface(oc), client.RoleBindingsNamespacer(oc), kclient.SecurityContextConstraintsInterface(kc), ), nil case buildapi.Kind("BuildConfig"): return buildreaper.NewBuildConfigReaper(oc), nil } return kReaperFunc(mapping) } kGenerators := w.Factory.Generators w.Generators = func(cmdName string) map[string]kubectl.Generator { originGenerators := DefaultGenerators(cmdName) kubeGenerators := kGenerators(cmdName) ret := map[string]kubectl.Generator{} for k, v := range kubeGenerators { ret[k] = v } for k, v := range originGenerators { ret[k] = v } return ret } kMapBasedSelectorForObjectFunc := w.Factory.MapBasedSelectorForObject w.MapBasedSelectorForObject = func(object runtime.Object) (string, error) { switch t := object.(type) { case *deployapi.DeploymentConfig: return kubectl.MakeLabels(t.Spec.Selector), nil default: return kMapBasedSelectorForObjectFunc(object) } } kPortsForObjectFunc := w.Factory.PortsForObject w.PortsForObject = func(object runtime.Object) ([]string, error) { switch t := object.(type) { case *deployapi.DeploymentConfig: return getPorts(t.Spec.Template.Spec), nil default: return kPortsForObjectFunc(object) } } kLogsForObjectFunc := w.Factory.LogsForObject w.LogsForObject = func(object, options runtime.Object) (*restclient.Request, error) { oc, _, err := w.Clients() if err != nil { return nil, err } switch t := object.(type) { case *deployapi.DeploymentConfig: dopts, ok := options.(*deployapi.DeploymentLogOptions) if !ok { return nil, errors.New("provided options object is not a DeploymentLogOptions") } return oc.DeploymentLogs(t.Namespace).Get(t.Name, *dopts), nil case *buildapi.Build: bopts, ok := options.(*buildapi.BuildLogOptions) if !ok { return nil, errors.New("provided options object is not a BuildLogOptions") } if bopts.Version != nil { return nil, errors.New("cannot specify a version and a build") } return oc.BuildLogs(t.Namespace).Get(t.Name, *bopts), nil case *buildapi.BuildConfig: bopts, ok := options.(*buildapi.BuildLogOptions) if !ok { return nil, errors.New("provided options object is not a BuildLogOptions") } builds, err := oc.Builds(t.Namespace).List(api.ListOptions{}) if err != nil { return nil, err } builds.Items = buildapi.FilterBuilds(builds.Items, buildapi.ByBuildConfigPredicate(t.Name)) if len(builds.Items) == 0 { return nil, fmt.Errorf("no builds found for %q", t.Name) } if bopts.Version != nil { // If a version has been specified, try to get the logs from that build. desired := buildutil.BuildNameForConfigVersion(t.Name, int(*bopts.Version)) return oc.BuildLogs(t.Namespace).Get(desired, *bopts), nil } sort.Sort(sort.Reverse(buildapi.BuildSliceByCreationTimestamp(builds.Items))) return oc.BuildLogs(t.Namespace).Get(builds.Items[0].Name, *bopts), nil default: return kLogsForObjectFunc(object, options) } } w.Printer = func(mapping *meta.RESTMapping, noHeaders, withNamespace, wide bool, showAll bool, showLabels, absoluteTimestamps bool, columnLabels []string) (kubectl.ResourcePrinter, error) { return describe.NewHumanReadablePrinter(noHeaders, withNamespace, wide, showAll, showLabels, absoluteTimestamps, columnLabels), nil } kCanBeExposed := w.Factory.CanBeExposed w.CanBeExposed = func(kind unversioned.GroupKind) error { if kind == deployapi.Kind("DeploymentConfig") { return nil } return kCanBeExposed(kind) } kCanBeAutoscaled := w.Factory.CanBeAutoscaled w.CanBeAutoscaled = func(kind unversioned.GroupKind) error { if kind == deployapi.Kind("DeploymentConfig") { return nil } return kCanBeAutoscaled(kind) } kAttachablePodForObjectFunc := w.Factory.AttachablePodForObject w.AttachablePodForObject = func(object runtime.Object) (*api.Pod, error) { switch t := object.(type) { case *deployapi.DeploymentConfig: _, kc, err := w.Clients() if err != nil { return nil, err } selector := labels.SelectorFromSet(t.Spec.Selector) f := func(pods []*api.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) } pod, _, err := cmdutil.GetFirstPod(kc, t.Namespace, selector, 1*time.Minute, f) return pod, err default: return kAttachablePodForObjectFunc(object) } } kProtocolsForObject := w.Factory.ProtocolsForObject w.ProtocolsForObject = func(object runtime.Object) (map[string]string, error) { switch t := object.(type) { case *deployapi.DeploymentConfig: return getProtocols(t.Spec.Template.Spec), nil default: return kProtocolsForObject(object) } } kSwaggerSchemaFunc := w.Factory.SwaggerSchema w.Factory.SwaggerSchema = func(gvk unversioned.GroupVersionKind) (*swagger.ApiDeclaration, error) { if !latest.OriginKind(gvk) { return kSwaggerSchemaFunc(gvk) } // TODO: we need to register the OpenShift API under the Kube group, and start returning the OpenShift // group from the scheme. oc, _, err := w.Clients() if err != nil { return nil, err } return w.OriginSwaggerSchema(oc.RESTClient, gvk.GroupVersion()) } w.EditorEnvs = func() []string { return []string{"OC_EDITOR", "EDITOR"} } w.PrintObjectSpecificMessage = func(obj runtime.Object, out io.Writer) {} kPauseObjectFunc := w.Factory.PauseObject w.Factory.PauseObject = func(object runtime.Object) (bool, error) { oc, _, err := w.Clients() if err != nil { return false, err } switch t := object.(type) { case *deployapi.DeploymentConfig: if t.Spec.Paused { return true, nil } t.Spec.Paused = true _, err := oc.DeploymentConfigs(t.Namespace).Update(t) // TODO: Pause the deployer containers. return false, err default: return kPauseObjectFunc(object) } } kResumeObjectFunc := w.Factory.ResumeObject w.Factory.ResumeObject = func(object runtime.Object) (bool, error) { oc, _, err := w.Clients() if err != nil { return false, err } switch t := object.(type) { case *deployapi.DeploymentConfig: if !t.Spec.Paused { return true, nil } t.Spec.Paused = false _, err := oc.DeploymentConfigs(t.Namespace).Update(t) // TODO: Resume the deployer containers. return false, err default: return kResumeObjectFunc(object) } } kHistoryViewerFunc := w.Factory.HistoryViewer w.Factory.HistoryViewer = func(mapping *meta.RESTMapping) (kubectl.HistoryViewer, error) { oc, kc, err := w.Clients() if err != nil { return nil, err } switch mapping.GroupVersionKind.GroupKind() { case deployapi.Kind("DeploymentConfig"): return deploycmd.NewDeploymentConfigHistoryViewer(oc, kc), nil } return kHistoryViewerFunc(mapping) } kRollbackerFunc := w.Factory.Rollbacker w.Factory.Rollbacker = func(mapping *meta.RESTMapping) (kubectl.Rollbacker, error) { oc, _, err := w.Clients() if err != nil { return nil, err } switch mapping.GroupVersionKind.GroupKind() { case deployapi.Kind("DeploymentConfig"): return deploycmd.NewDeploymentConfigRollbacker(oc), nil } return kRollbackerFunc(mapping) } return w }
// NewFactory creates an object that holds common methods across all OpenShift commands func NewFactory(clientConfig kclientcmd.ClientConfig) *Factory { mapper := ShortcutExpander{kubectl.ShortcutExpander{latest.RESTMapper}} clients := &clientCache{ clients: make(map[string]*client.Client), loader: clientConfig, } generators := map[string]kubectl.Generator{ "route/v1": routegen.RouteGenerator{}, "run/v1": deploygen.BasicDeploymentConfigController{}, "run-controller/v1": kubectl.BasicReplicationController{}, } w := &Factory{ Factory: cmdutil.NewFactory(clientConfig), OpenShiftClientConfig: clientConfig, clients: clients, } w.Object = func() (meta.RESTMapper, runtime.ObjectTyper) { if cfg, err := clientConfig.ClientConfig(); err == nil { return kubectl.OutputVersionMapper{mapper, cfg.Version}, api.Scheme } return mapper, api.Scheme } kRESTClient := w.Factory.RESTClient w.RESTClient = func(mapping *meta.RESTMapping) (resource.RESTClient, error) { if latest.OriginKind(mapping.Kind, mapping.APIVersion) { client, err := clients.ClientForVersion(mapping.APIVersion) if err != nil { return nil, err } return client.RESTClient, nil } return kRESTClient(mapping) } // Save original Describer function kDescriberFunc := w.Factory.Describer w.Describer = func(mapping *meta.RESTMapping) (kubectl.Describer, error) { if latest.OriginKind(mapping.Kind, mapping.APIVersion) { oClient, kClient, err := w.Clients() if err != nil { return nil, fmt.Errorf("unable to create client %s: %v", mapping.Kind, err) } cfg, err := clients.ClientConfigForVersion(mapping.APIVersion) if err != nil { return nil, fmt.Errorf("unable to load a client %s: %v", mapping.Kind, err) } describer, ok := describe.DescriberFor(mapping.Kind, oClient, kClient, cfg.Host) if !ok { return nil, fmt.Errorf("no description has been implemented for %q", mapping.Kind) } return describer, nil } return kDescriberFunc(mapping) } kScalerFunc := w.Factory.Scaler w.Scaler = func(mapping *meta.RESTMapping) (kubectl.Scaler, error) { oc, kc, err := w.Clients() if err != nil { return nil, err } if mapping.Kind == "DeploymentConfig" { return deployscaler.NewDeploymentConfigScaler(oc, kc), nil } return kScalerFunc(mapping) } kReaperFunc := w.Factory.Reaper w.Reaper = func(mapping *meta.RESTMapping) (kubectl.Reaper, error) { oc, kc, err := w.Clients() if err != nil { return nil, err } if mapping.Kind == "DeploymentConfig" { return deployreaper.NewDeploymentConfigReaper(oc, kc), nil } return kReaperFunc(mapping) } kGeneratorFunc := w.Factory.Generator w.Generator = func(name string) (kubectl.Generator, bool) { if generator, ok := generators[name]; ok { return generator, true } return kGeneratorFunc(name) } kPodSelectorForObjectFunc := w.Factory.PodSelectorForObject w.PodSelectorForObject = func(object runtime.Object) (string, error) { switch t := object.(type) { case *deployapi.DeploymentConfig: return kubectl.MakeLabels(t.Template.ControllerTemplate.Selector), nil default: return kPodSelectorForObjectFunc(object) } } kPortsForObjectFunc := w.Factory.PortsForObject w.PortsForObject = func(object runtime.Object) ([]string, error) { switch t := object.(type) { case *deployapi.DeploymentConfig: return getPorts(t.Template.ControllerTemplate.Template.Spec), nil default: return kPortsForObjectFunc(object) } } w.Printer = func(mapping *meta.RESTMapping, noHeaders, withNamespace, wide bool, columnLabels []string) (kubectl.ResourcePrinter, error) { return describe.NewHumanReadablePrinter(noHeaders, withNamespace, wide, columnLabels), nil } return w }
// NewFactory creates a factory with the default Kubernetes resources defined // if optionalClientConfig is nil, then flags will be bound to a new clientcmd.ClientConfig. // if optionalClientConfig is not nil, then this factory will make use of it. func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { mapper := kubectl.ShortcutExpander{RESTMapper: api.RESTMapper} flags := pflag.NewFlagSet("", pflag.ContinueOnError) flags.SetNormalizeFunc(util.WarnWordSepNormalizeFunc) // Warn for "_" flags generators := map[string]kubectl.Generator{ "run/v1": kubectl.BasicReplicationController{}, "run-pod/v1": kubectl.BasicPod{}, "service/v1": kubectl.ServiceGeneratorV1{}, "service/v2": kubectl.ServiceGeneratorV2{}, "horizontalpodautoscaler/v1beta1": kubectl.HorizontalPodAutoscalerV1Beta1{}, } clientConfig := optionalClientConfig if optionalClientConfig == nil { clientConfig = DefaultClientConfig(flags) } clients := NewClientCache(clientConfig) return &Factory{ clients: clients, flags: flags, generators: generators, Object: func() (meta.RESTMapper, runtime.ObjectTyper) { cfg, err := clientConfig.ClientConfig() CheckErr(err) cmdApiVersion := cfg.Version return kubectl.OutputVersionMapper{RESTMapper: mapper, OutputVersion: cmdApiVersion}, api.Scheme }, Client: func() (*client.Client, error) { return clients.ClientForVersion("") }, ClientConfig: func() (*client.Config, error) { return clients.ClientConfigForVersion("") }, RESTClient: func(mapping *meta.RESTMapping) (resource.RESTClient, error) { group, err := api.RESTMapper.GroupForResource(mapping.Resource) if err != nil { return nil, err } client, err := clients.ClientForVersion(mapping.APIVersion) if err != nil { return nil, err } switch group { case "": return client.RESTClient, nil case "extensions": return client.ExtensionsClient.RESTClient, nil } return nil, fmt.Errorf("unable to get RESTClient for resource '%s'", mapping.Resource) }, Describer: func(mapping *meta.RESTMapping) (kubectl.Describer, error) { group, err := api.RESTMapper.GroupForResource(mapping.Resource) if err != nil { return nil, err } client, err := clients.ClientForVersion(mapping.APIVersion) if err != nil { return nil, err } if describer, ok := kubectl.DescriberFor(group, mapping.Kind, client); ok { return describer, nil } return nil, fmt.Errorf("no description has been implemented for %q", mapping.Kind) }, Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll bool, columnLabels []string) (kubectl.ResourcePrinter, error) { return kubectl.NewHumanReadablePrinter(noHeaders, withNamespace, wide, showAll, columnLabels), nil }, PodSelectorForObject: func(object runtime.Object) (string, error) { // TODO: replace with a swagger schema based approach (identify pod selector via schema introspection) switch t := object.(type) { case *api.ReplicationController: return kubectl.MakeLabels(t.Spec.Selector), nil case *api.Pod: if len(t.Labels) == 0 { return "", fmt.Errorf("the pod has no labels and cannot be exposed") } return kubectl.MakeLabels(t.Labels), nil case *api.Service: if t.Spec.Selector == nil { return "", fmt.Errorf("the service has no pod selector set") } return kubectl.MakeLabels(t.Spec.Selector), nil default: _, kind, err := api.Scheme.ObjectVersionAndKind(object) if err != nil { return "", err } return "", fmt.Errorf("cannot extract pod selector from %s", kind) } }, PortsForObject: func(object runtime.Object) ([]string, error) { // TODO: replace with a swagger schema based approach (identify pod selector via schema introspection) switch t := object.(type) { case *api.ReplicationController: return getPorts(t.Spec.Template.Spec), nil case *api.Pod: return getPorts(t.Spec), nil case *api.Service: return getServicePorts(t.Spec), nil default: _, kind, err := api.Scheme.ObjectVersionAndKind(object) if err != nil { return nil, err } return nil, fmt.Errorf("cannot extract ports from %s", kind) } }, LabelsForObject: func(object runtime.Object) (map[string]string, error) { return meta.NewAccessor().Labels(object) }, Scaler: func(mapping *meta.RESTMapping) (kubectl.Scaler, error) { client, err := clients.ClientForVersion(mapping.APIVersion) if err != nil { return nil, err } return kubectl.ScalerFor(mapping.Kind, client) }, Reaper: func(mapping *meta.RESTMapping) (kubectl.Reaper, error) { client, err := clients.ClientForVersion(mapping.APIVersion) if err != nil { return nil, err } return kubectl.ReaperFor(mapping.Kind, client) }, Validator: func(validate bool, cacheDir string) (validation.Schema, error) { if validate { client, err := clients.ClientForVersion("") if err != nil { return nil, err } dir := cacheDir if len(dir) > 0 { version, err := client.ServerVersion() if err != nil { return nil, err } dir = path.Join(cacheDir, version.String()) } return &clientSwaggerSchema{ c: client, cacheDir: dir, mapper: api.RESTMapper, }, nil } return validation.NullSchema{}, nil }, DefaultNamespace: func() (string, bool, error) { return clientConfig.Namespace() }, Generator: func(name string) (kubectl.Generator, bool) { generator, ok := generators[name] return generator, ok }, CanBeExposed: func(kind string) error { switch kind { case "ReplicationController", "Service", "Pod": // nothing to do here default: return fmt.Errorf("cannot expose a %s", kind) } return nil }, CanBeAutoscaled: func(kind string) error { switch kind { // TODO: support autoscale for deployments case "ReplicationController": // nothing to do here default: return fmt.Errorf("cannot autoscale a %s", kind) } return nil }, AttachablePodForObject: func(object runtime.Object) (*api.Pod, error) { client, err := clients.ClientForVersion("") if err != nil { return nil, err } switch t := object.(type) { case *api.ReplicationController: var pods *api.PodList for pods == nil || len(pods.Items) == 0 { var err error if pods, err = client.Pods(t.Namespace).List(labels.SelectorFromSet(t.Spec.Selector), fields.Everything()); err != nil { return nil, err } if len(pods.Items) == 0 { time.Sleep(2 * time.Second) } } pod := &pods.Items[0] return pod, nil case *api.Pod: return t, nil default: _, kind, err := api.Scheme.ObjectVersionAndKind(object) if err != nil { return nil, err } return nil, fmt.Errorf("cannot attach to %s: not implemented", kind) } }, } }
// NewFactory creates a factory with the default Kubernetes resources defined // if optionalClientConfig is nil, then flags will be bound to a new clientcmd.ClientConfig. // if optionalClientConfig is not nil, then this factory will make use of it. func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { mapper := kubectl.ShortcutExpander{RESTMapper: registered.RESTMapper()} flags := pflag.NewFlagSet("", pflag.ContinueOnError) flags.SetNormalizeFunc(utilflag.WarnWordSepNormalizeFunc) // Warn for "_" flags clientConfig := optionalClientConfig if optionalClientConfig == nil { clientConfig = DefaultClientConfig(flags) } clients := NewClientCache(clientConfig) return &Factory{ clients: clients, flags: flags, // If discoverDynamicAPIs is true, make API calls to the discovery service to find APIs that // have been dynamically added to the apiserver Object: func(discoverDynamicAPIs bool) (meta.RESTMapper, runtime.ObjectTyper) { cfg, err := clientConfig.ClientConfig() checkErrWithPrefix("failed to get client config: ", err) cmdApiVersion := unversioned.GroupVersion{} if cfg.GroupVersion != nil { cmdApiVersion = *cfg.GroupVersion } if discoverDynamicAPIs { client, err := clients.ClientForVersion(&unversioned.GroupVersion{Version: "v1"}) checkErrWithPrefix("failed to find client for version v1: ", err) var versions []unversioned.GroupVersion var gvks []unversioned.GroupVersionKind retries := 3 for i := 0; i < retries; i++ { versions, gvks, err = GetThirdPartyGroupVersions(client.Discovery()) // Retry if we got a NotFound error, because user may delete // a thirdparty group when the GetThirdPartyGroupVersions is // running. if err == nil || !apierrors.IsNotFound(err) { break } } checkErrWithPrefix("failed to get third-party group versions: ", err) if len(versions) > 0 { priorityMapper, ok := mapper.RESTMapper.(meta.PriorityRESTMapper) if !ok { CheckErr(fmt.Errorf("expected PriorityMapper, saw: %v", mapper.RESTMapper)) return nil, nil } multiMapper, ok := priorityMapper.Delegate.(meta.MultiRESTMapper) if !ok { CheckErr(fmt.Errorf("unexpected type: %v", mapper.RESTMapper)) return nil, nil } groupsMap := map[string][]unversioned.GroupVersion{} for _, version := range versions { groupsMap[version.Group] = append(groupsMap[version.Group], version) } for group, versionList := range groupsMap { preferredExternalVersion := versionList[0] thirdPartyMapper, err := kubectl.NewThirdPartyResourceMapper(versionList, getGroupVersionKinds(gvks, group)) checkErrWithPrefix("failed to create third party resource mapper: ", err) accessor := meta.NewAccessor() groupMeta := apimachinery.GroupMeta{ GroupVersion: preferredExternalVersion, GroupVersions: versionList, RESTMapper: thirdPartyMapper, SelfLinker: runtime.SelfLinker(accessor), InterfacesFor: makeInterfacesFor(versionList), } checkErrWithPrefix("failed to register group: ", registered.RegisterGroup(groupMeta)) registered.AddThirdPartyAPIGroupVersions(versionList...) multiMapper = append(meta.MultiRESTMapper{thirdPartyMapper}, multiMapper...) } priorityMapper.Delegate = multiMapper // Reassign to the RESTMapper here because priorityMapper is actually a copy, so if we // don't reassign, the above assignement won't actually update mapper.RESTMapper mapper.RESTMapper = priorityMapper } } outputRESTMapper := kubectl.OutputVersionMapper{RESTMapper: mapper, OutputVersions: []unversioned.GroupVersion{cmdApiVersion}} priorityRESTMapper := meta.PriorityRESTMapper{ Delegate: outputRESTMapper, } // TODO: this should come from registered versions groups := []string{api.GroupName, autoscaling.GroupName, extensions.GroupName, federation.GroupName, batch.GroupName} // set a preferred version for _, group := range groups { gvs := registered.EnabledVersionsForGroup(group) if len(gvs) == 0 { continue } priorityRESTMapper.ResourcePriority = append(priorityRESTMapper.ResourcePriority, unversioned.GroupVersionResource{Group: group, Version: gvs[0].Version, Resource: meta.AnyResource}) priorityRESTMapper.KindPriority = append(priorityRESTMapper.KindPriority, unversioned.GroupVersionKind{Group: group, Version: gvs[0].Version, Kind: meta.AnyKind}) } for _, group := range groups { priorityRESTMapper.ResourcePriority = append(priorityRESTMapper.ResourcePriority, unversioned.GroupVersionResource{Group: group, Version: meta.AnyVersion, Resource: meta.AnyResource}) priorityRESTMapper.KindPriority = append(priorityRESTMapper.KindPriority, unversioned.GroupVersionKind{Group: group, Version: meta.AnyVersion, Kind: meta.AnyKind}) } return priorityRESTMapper, api.Scheme }, Client: func() (*client.Client, error) { return clients.ClientForVersion(nil) }, ClientConfig: func() (*restclient.Config, error) { return clients.ClientConfigForVersion(nil) }, ClientForMapping: func(mapping *meta.RESTMapping) (resource.RESTClient, error) { cfg, err := clientConfig.ClientConfig() if err != nil { return nil, err } if err := client.SetKubernetesDefaults(cfg); err != nil { return nil, err } gvk := mapping.GroupVersionKind switch gvk.Group { case federation.GroupName: mappingVersion := mapping.GroupVersionKind.GroupVersion() return clients.FederationClientForVersion(&mappingVersion) case api.GroupName: cfg.APIPath = "/api" default: cfg.APIPath = "/apis" } gv := gvk.GroupVersion() cfg.GroupVersion = &gv if registered.IsThirdPartyAPIGroupVersion(gvk.GroupVersion()) { cfg.NegotiatedSerializer = thirdpartyresourcedata.NewNegotiatedSerializer(api.Codecs, gvk.Kind, gv, gv) } return restclient.RESTClientFor(cfg) }, Describer: func(mapping *meta.RESTMapping) (kubectl.Describer, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() if mapping.GroupVersionKind.Group == federation.GroupName { fedClientSet, err := clients.FederationClientSetForVersion(&mappingVersion) if err != nil { return nil, err } if mapping.GroupVersionKind.Kind == "Cluster" { return &kubectl.ClusterDescriber{Interface: fedClientSet}, nil } } client, err := clients.ClientForVersion(&mappingVersion) if err != nil { return nil, err } if describer, ok := kubectl.DescriberFor(mapping.GroupVersionKind.GroupKind(), client); ok { return describer, nil } return nil, fmt.Errorf("no description has been implemented for %q", mapping.GroupVersionKind.Kind) }, Decoder: func(toInternal bool) runtime.Decoder { var decoder runtime.Decoder if toInternal { decoder = api.Codecs.UniversalDecoder() } else { decoder = api.Codecs.UniversalDeserializer() } return thirdpartyresourcedata.NewDecoder(decoder, "") }, JSONEncoder: func() runtime.Encoder { return api.Codecs.LegacyCodec(registered.EnabledVersions()...) }, Printer: func(mapping *meta.RESTMapping, options kubectl.PrintOptions) (kubectl.ResourcePrinter, error) { return kubectl.NewHumanReadablePrinter(options), nil }, MapBasedSelectorForObject: func(object runtime.Object) (string, error) { // TODO: replace with a swagger schema based approach (identify pod selector via schema introspection) switch t := object.(type) { case *api.ReplicationController: return kubectl.MakeLabels(t.Spec.Selector), nil case *api.Pod: if len(t.Labels) == 0 { return "", fmt.Errorf("the pod has no labels and cannot be exposed") } return kubectl.MakeLabels(t.Labels), nil case *api.Service: if t.Spec.Selector == nil { return "", fmt.Errorf("the service has no pod selector set") } return kubectl.MakeLabels(t.Spec.Selector), nil case *extensions.Deployment: // TODO(madhusudancs): Make this smarter by admitting MatchExpressions with Equals // operator, DoubleEquals operator and In operator with only one element in the set. if len(t.Spec.Selector.MatchExpressions) > 0 { return "", fmt.Errorf("couldn't convert expressions - \"%+v\" to map-based selector format", t.Spec.Selector.MatchExpressions) } return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil case *extensions.ReplicaSet: // TODO(madhusudancs): Make this smarter by admitting MatchExpressions with Equals // operator, DoubleEquals operator and In operator with only one element in the set. if len(t.Spec.Selector.MatchExpressions) > 0 { return "", fmt.Errorf("couldn't convert expressions - \"%+v\" to map-based selector format", t.Spec.Selector.MatchExpressions) } return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil default: gvks, _, err := api.Scheme.ObjectKinds(object) if err != nil { return "", err } return "", fmt.Errorf("cannot extract pod selector from %v", gvks[0]) } }, PortsForObject: func(object runtime.Object) ([]string, error) { // TODO: replace with a swagger schema based approach (identify pod selector via schema introspection) switch t := object.(type) { case *api.ReplicationController: return getPorts(t.Spec.Template.Spec), nil case *api.Pod: return getPorts(t.Spec), nil case *api.Service: return getServicePorts(t.Spec), nil case *extensions.Deployment: return getPorts(t.Spec.Template.Spec), nil case *extensions.ReplicaSet: return getPorts(t.Spec.Template.Spec), nil default: gvks, _, err := api.Scheme.ObjectKinds(object) if err != nil { return nil, err } return nil, fmt.Errorf("cannot extract ports from %v", gvks[0]) } }, ProtocolsForObject: func(object runtime.Object) (map[string]string, error) { // TODO: replace with a swagger schema based approach (identify pod selector via schema introspection) switch t := object.(type) { case *api.ReplicationController: return getProtocols(t.Spec.Template.Spec), nil case *api.Pod: return getProtocols(t.Spec), nil case *api.Service: return getServiceProtocols(t.Spec), nil case *extensions.Deployment: return getProtocols(t.Spec.Template.Spec), nil case *extensions.ReplicaSet: return getProtocols(t.Spec.Template.Spec), nil default: gvks, _, err := api.Scheme.ObjectKinds(object) if err != nil { return nil, err } return nil, fmt.Errorf("cannot extract protocols from %v", gvks[0]) } }, LabelsForObject: func(object runtime.Object) (map[string]string, error) { return meta.NewAccessor().Labels(object) }, LogsForObject: func(object, options runtime.Object) (*restclient.Request, error) { c, err := clients.ClientForVersion(nil) if err != nil { return nil, err } switch t := object.(type) { case *api.Pod: opts, ok := options.(*api.PodLogOptions) if !ok { return nil, errors.New("provided options object is not a PodLogOptions") } return c.Pods(t.Namespace).GetLogs(t.Name, opts), nil case *api.ReplicationController: opts, ok := options.(*api.PodLogOptions) if !ok { return nil, errors.New("provided options object is not a PodLogOptions") } selector := labels.SelectorFromSet(t.Spec.Selector) sortBy := func(pods []*api.Pod) sort.Interface { return controller.ByLogging(pods) } pod, numPods, err := GetFirstPod(c, t.Namespace, selector, 20*time.Second, sortBy) if err != nil { return nil, err } if numPods > 1 { fmt.Fprintf(os.Stderr, "Found %v pods, using pod/%v\n", numPods, pod.Name) } return c.Pods(pod.Namespace).GetLogs(pod.Name, opts), nil case *extensions.ReplicaSet: opts, ok := options.(*api.PodLogOptions) if !ok { return nil, errors.New("provided options object is not a PodLogOptions") } selector, err := unversioned.LabelSelectorAsSelector(t.Spec.Selector) if err != nil { return nil, fmt.Errorf("invalid label selector: %v", err) } sortBy := func(pods []*api.Pod) sort.Interface { return controller.ByLogging(pods) } pod, numPods, err := GetFirstPod(c, t.Namespace, selector, 20*time.Second, sortBy) if err != nil { return nil, err } if numPods > 1 { fmt.Fprintf(os.Stderr, "Found %v pods, using pod/%v\n", numPods, pod.Name) } return c.Pods(pod.Namespace).GetLogs(pod.Name, opts), nil default: gvks, _, err := api.Scheme.ObjectKinds(object) if err != nil { return nil, err } return nil, fmt.Errorf("cannot get the logs from %v", gvks[0]) } }, PauseObject: func(object runtime.Object) (bool, error) { c, err := clients.ClientForVersion(nil) if err != nil { return false, err } switch t := object.(type) { case *extensions.Deployment: if t.Spec.Paused { return true, nil } t.Spec.Paused = true _, err := c.Extensions().Deployments(t.Namespace).Update(t) return false, err default: gvks, _, err := api.Scheme.ObjectKinds(object) if err != nil { return false, err } return false, fmt.Errorf("cannot pause %v", gvks[0]) } }, ResumeObject: func(object runtime.Object) (bool, error) { c, err := clients.ClientForVersion(nil) if err != nil { return false, err } switch t := object.(type) { case *extensions.Deployment: if !t.Spec.Paused { return true, nil } t.Spec.Paused = false _, err := c.Extensions().Deployments(t.Namespace).Update(t) return false, err default: gvks, _, err := api.Scheme.ObjectKinds(object) if err != nil { return false, err } return false, fmt.Errorf("cannot resume %v", gvks[0]) } }, Scaler: func(mapping *meta.RESTMapping) (kubectl.Scaler, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() client, err := clients.ClientForVersion(&mappingVersion) if err != nil { return nil, err } return kubectl.ScalerFor(mapping.GroupVersionKind.GroupKind(), client) }, Reaper: func(mapping *meta.RESTMapping) (kubectl.Reaper, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() client, err := clients.ClientForVersion(&mappingVersion) if err != nil { return nil, err } return kubectl.ReaperFor(mapping.GroupVersionKind.GroupKind(), client) }, HistoryViewer: func(mapping *meta.RESTMapping) (kubectl.HistoryViewer, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() client, err := clients.ClientForVersion(&mappingVersion) clientset := clientset.FromUnversionedClient(client) if err != nil { return nil, err } return kubectl.HistoryViewerFor(mapping.GroupVersionKind.GroupKind(), clientset) }, Rollbacker: func(mapping *meta.RESTMapping) (kubectl.Rollbacker, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() client, err := clients.ClientForVersion(&mappingVersion) if err != nil { return nil, err } return kubectl.RollbackerFor(mapping.GroupVersionKind.GroupKind(), client) }, StatusViewer: func(mapping *meta.RESTMapping) (kubectl.StatusViewer, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() client, err := clients.ClientForVersion(&mappingVersion) if err != nil { return nil, err } return kubectl.StatusViewerFor(mapping.GroupVersionKind.GroupKind(), client) }, Validator: func(validate bool, cacheDir string) (validation.Schema, error) { if validate { client, err := clients.ClientForVersion(nil) if err != nil { return nil, err } dir := cacheDir if len(dir) > 0 { version, err := client.ServerVersion() if err != nil { return nil, err } dir = path.Join(cacheDir, version.String()) } fedClient, err := clients.FederationClientForVersion(nil) if err != nil { return nil, err } return &clientSwaggerSchema{ c: client, fedc: fedClient, cacheDir: dir, mapper: api.RESTMapper, }, nil } return validation.NullSchema{}, nil }, SwaggerSchema: func(gvk unversioned.GroupVersionKind) (*swagger.ApiDeclaration, error) { version := gvk.GroupVersion() client, err := clients.ClientForVersion(&version) if err != nil { return nil, err } return client.Discovery().SwaggerSchema(version) }, DefaultNamespace: func() (string, bool, error) { return clientConfig.Namespace() }, Generators: func(cmdName string) map[string]kubectl.Generator { return DefaultGenerators(cmdName) }, CanBeExposed: func(kind unversioned.GroupKind) error { switch kind { case api.Kind("ReplicationController"), api.Kind("Service"), api.Kind("Pod"), extensions.Kind("Deployment"), extensions.Kind("ReplicaSet"): // nothing to do here default: return fmt.Errorf("cannot expose a %s", kind) } return nil }, CanBeAutoscaled: func(kind unversioned.GroupKind) error { switch kind { case api.Kind("ReplicationController"), extensions.Kind("Deployment"), extensions.Kind("ReplicaSet"): // nothing to do here default: return fmt.Errorf("cannot autoscale a %v", kind) } return nil }, AttachablePodForObject: func(object runtime.Object) (*api.Pod, error) { client, err := clients.ClientForVersion(nil) if err != nil { return nil, err } switch t := object.(type) { case *api.ReplicationController: selector := labels.SelectorFromSet(t.Spec.Selector) sortBy := func(pods []*api.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) } pod, _, err := GetFirstPod(client, t.Namespace, selector, 1*time.Minute, sortBy) return pod, err case *extensions.Deployment: selector, err := unversioned.LabelSelectorAsSelector(t.Spec.Selector) if err != nil { return nil, fmt.Errorf("invalid label selector: %v", err) } sortBy := func(pods []*api.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) } pod, _, err := GetFirstPod(client, t.Namespace, selector, 1*time.Minute, sortBy) return pod, err case *batch.Job: selector, err := unversioned.LabelSelectorAsSelector(t.Spec.Selector) if err != nil { return nil, fmt.Errorf("invalid label selector: %v", err) } sortBy := func(pods []*api.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) } pod, _, err := GetFirstPod(client, t.Namespace, selector, 1*time.Minute, sortBy) return pod, err case *api.Pod: return t, nil default: gvks, _, err := api.Scheme.ObjectKinds(object) if err != nil { return nil, err } return nil, fmt.Errorf("cannot attach to %v: not implemented", gvks[0]) } }, // UpdatePodSpecForObject update the pod specification for the provided object UpdatePodSpecForObject: func(obj runtime.Object, fn func(*api.PodSpec) error) (bool, error) { // TODO: replace with a swagger schema based approach (identify pod template via schema introspection) switch t := obj.(type) { case *api.Pod: return true, fn(&t.Spec) case *api.ReplicationController: if t.Spec.Template == nil { t.Spec.Template = &api.PodTemplateSpec{} } return true, fn(&t.Spec.Template.Spec) case *extensions.Deployment: return true, fn(&t.Spec.Template.Spec) case *extensions.DaemonSet: return true, fn(&t.Spec.Template.Spec) case *extensions.ReplicaSet: return true, fn(&t.Spec.Template.Spec) case *apps.PetSet: return true, fn(&t.Spec.Template.Spec) case *batch.Job: return true, fn(&t.Spec.Template.Spec) default: return false, fmt.Errorf("the object is not a pod or does not have a pod template") } }, EditorEnvs: func() []string { return []string{"KUBE_EDITOR", "EDITOR"} }, PrintObjectSpecificMessage: func(obj runtime.Object, out io.Writer) { switch obj := obj.(type) { case *api.Service: if obj.Spec.Type == api.ServiceTypeNodePort { msg := fmt.Sprintf( `You have exposed your service on an external port on all nodes in your cluster. If you want to expose this service to the external internet, you may need to set up firewall rules for the service port(s) (%s) to serve traffic. See http://releases.k8s.io/HEAD/docs/user-guide/services-firewalls.md for more details. `, makePortsString(obj.Spec.Ports, true)) out.Write([]byte(msg)) } if _, ok := obj.Annotations[service.AnnotationLoadBalancerSourceRangesKey]; ok { msg := fmt.Sprintf( `You are using service annotation [service.beta.kubernetes.io/load-balancer-source-ranges]. It has been promoted to field [loadBalancerSourceRanges] in service spec. This annotation will be deprecated in the future. Please use the loadBalancerSourceRanges field instead. See http://releases.k8s.io/HEAD/docs/user-guide/services-firewalls.md for more details. `) out.Write([]byte(msg)) } } }, } }
func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error { namespace, _, err := f.DefaultNamespace() if err != nil { return err } mapper, typer := f.Object() r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). ContinueOnError(). NamespaceParam(namespace).DefaultNamespace(). ResourceTypeOrNameArgs(false, args...). Flatten(). Do() err = r.Err() if err != nil { return err } mapping, err := r.ResourceMapping() if err != nil { return err } infos, err := r.Infos() if err != nil { return err } if len(infos) > 1 { return fmt.Errorf("multiple resources provided: %v", args) } info := infos[0] // Get the input object client, err := f.RESTClient(mapping) if err != nil { return err } inputObject, err := resource.NewHelper(client, mapping).Get(info.Namespace, info.Name) if err != nil { return err } // Get the generator, setup and validate all required parameters generatorName := cmdutil.GetFlagString(cmd, "generator") generator, found := f.Generator(generatorName) if !found { return cmdutil.UsageError(cmd, fmt.Sprintf("generator %q not found.", generatorName)) } names := generator.ParamNames() params := kubectl.MakeParams(cmd, names) params["default-name"] = info.Name if s, found := params["selector"]; !found || len(s) == 0 || cmdutil.GetFlagInt(cmd, "port") < 1 { if len(s) == 0 { s, err := f.PodSelectorForObject(inputObject) if err != nil { return cmdutil.UsageError(cmd, fmt.Sprintf("couldn't find selectors via --selector flag or introspection: %s", err)) } params["selector"] = s } noPorts := true for _, param := range names { if param.Name == "port" { noPorts = false break } } if cmdutil.GetFlagInt(cmd, "port") < 0 && !noPorts { ports, err := f.PortsForObject(inputObject) if err != nil { return cmdutil.UsageError(cmd, fmt.Sprintf("couldn't find port via --port flag or introspection: %s", err)) } switch len(ports) { case 0: return cmdutil.UsageError(cmd, "couldn't find port via --port flag or introspection") case 1: params["port"] = ports[0] default: return cmdutil.UsageError(cmd, fmt.Sprintf("multiple ports to choose from: %v, please explicitly specify a port using the --port flag.", ports)) } } } if cmdutil.GetFlagBool(cmd, "create-external-load-balancer") { params["create-external-load-balancer"] = "true" } if len(params["labels"]) == 0 { labels, err := f.LabelsForObject(inputObject) if err != nil { return err } params["labels"] = kubectl.MakeLabels(labels) } if v := cmdutil.GetFlagString(cmd, "type"); v != "" { params["type"] = v } err = kubectl.ValidateParams(names, params) if err != nil { return err } // Expose new object object, err := generator.Generate(params) if err != nil { return err } inline := cmdutil.GetFlagString(cmd, "overrides") if len(inline) > 0 { object, err = cmdutil.Merge(object, inline, mapping.Kind) if err != nil { return err } } // TODO: extract this flag to a central location, when such a location exists. if !cmdutil.GetFlagBool(cmd, "dry-run") { resourceMapper := &resource.Mapper{ObjectTyper: typer, RESTMapper: mapper, ClientMapper: f.ClientMapperForCommand()} info, err := resourceMapper.InfoForObject(object) if err != nil { return err } data, err := info.Mapping.Codec.Encode(object) if err != nil { return err } _, err = resource.NewHelper(info.Client, info.Mapping).Create(namespace, false, data) if err != nil { return err } } return f.PrintObject(cmd, object, out) }
func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *ExposeOptions) error { namespace, enforceNamespace, err := f.DefaultNamespace() if err != nil { return err } mapper, typer := f.Object(false) r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(namespace).DefaultNamespace(). FilenameParam(enforceNamespace, options.Recursive, options.Filenames...). ResourceTypeOrNameArgs(false, args...). Flatten(). Do() err = r.Err() if err != nil { return err } // Get the generator, setup and validate all required parameters generatorName := cmdutil.GetFlagString(cmd, "generator") generators := f.Generators("expose") generator, found := generators[generatorName] if !found { return cmdutil.UsageError(cmd, fmt.Sprintf("generator %q not found.", generatorName)) } names := generator.ParamNames() err = r.Visit(func(info *resource.Info, err error) error { if err != nil { return err } mapping := info.ResourceMapping() if err := f.CanBeExposed(mapping.GroupVersionKind.GroupKind()); err != nil { return err } params := kubectl.MakeParams(cmd, names) name := info.Name if len(name) > validation.DNS952LabelMaxLength { name = name[:validation.DNS952LabelMaxLength] } params["default-name"] = name // For objects that need a pod selector, derive it from the exposed object in case a user // didn't explicitly specify one via --selector if s, found := params["selector"]; found && kubectl.IsZero(s) { s, err := f.MapBasedSelectorForObject(info.Object) if err != nil { return cmdutil.UsageError(cmd, fmt.Sprintf("couldn't retrieve selectors via --selector flag or introspection: %s", err)) } params["selector"] = s } // For objects that need a port, derive it from the exposed object in case a user // didn't explicitly specify one via --port if port, found := params["port"]; found && kubectl.IsZero(port) { ports, err := f.PortsForObject(info.Object) if err != nil { return cmdutil.UsageError(cmd, fmt.Sprintf("couldn't find port via --port flag or introspection: %s", err)) } switch len(ports) { case 0: return cmdutil.UsageError(cmd, "couldn't find port via --port flag or introspection") case 1: params["port"] = ports[0] default: params["ports"] = strings.Join(ports, ",") } } // Always try to derive protocols from the exposed object, may use // different protocols for different ports. if _, found := params["protocol"]; found { protocolsMap, err := f.ProtocolsForObject(info.Object) if err != nil { return cmdutil.UsageError(cmd, fmt.Sprintf("couldn't find protocol via introspection: %s", err)) } if protocols := kubectl.MakeProtocols(protocolsMap); !kubectl.IsZero(protocols) { params["protocols"] = protocols } } if kubectl.IsZero(params["labels"]) { labels, err := f.LabelsForObject(info.Object) if err != nil { return err } params["labels"] = kubectl.MakeLabels(labels) } if err = kubectl.ValidateParams(names, params); err != nil { return err } // Check for invalid flags used against the present generator. if err := kubectl.EnsureFlagsValid(cmd, generators, generatorName); err != nil { return err } // Generate new object object, err := generator.Generate(params) if err != nil { return err } if inline := cmdutil.GetFlagString(cmd, "overrides"); len(inline) > 0 { codec := runtime.NewCodec(f.JSONEncoder(), f.Decoder(true)) object, err = cmdutil.Merge(codec, object, inline, mapping.GroupVersionKind.Kind) if err != nil { return err } } resourceMapper := &resource.Mapper{ ObjectTyper: typer, RESTMapper: mapper, ClientMapper: resource.ClientMapperFunc(f.ClientForMapping), Decoder: f.Decoder(true), } info, err = resourceMapper.InfoForObject(object, nil) if err != nil { return err } if cmdutil.ShouldRecord(cmd, info) { if err := cmdutil.RecordChangeCause(object, f.Command()); err != nil { return err } } info.Refresh(object, true) if cmdutil.GetDryRunFlag(cmd) { return f.PrintObject(cmd, mapper, object, out) } if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil { return err } // Serialize the object with the annotation applied. object, err = resource.NewHelper(info.Client, info.Mapping).Create(namespace, false, object) if err != nil { return err } if len(cmdutil.GetFlagString(cmd, "output")) > 0 { return f.PrintObject(cmd, mapper, object, out) } cmdutil.PrintSuccess(mapper, false, out, info.Mapping.Resource, info.Name, "exposed") return nil }) if err != nil { return err } return nil }
// NewFactory creates an object that holds common methods across all OpenShift commands func NewFactory(clientConfig kclientcmd.ClientConfig) *Factory { var restMapper meta.MultiRESTMapper seenGroups := sets.String{} for _, gv := range registered.EnabledVersions() { if seenGroups.Has(gv.Group) { continue } seenGroups.Insert(gv.Group) groupMeta, err := registered.Group(gv.Group) if err != nil { continue } restMapper = meta.MultiRESTMapper(append(restMapper, groupMeta.RESTMapper)) } mapper := ShortcutExpander{RESTMapper: kubectl.ShortcutExpander{RESTMapper: restMapper}} clients := &clientCache{ clients: make(map[string]*client.Client), configs: make(map[string]*kclient.Config), loader: clientConfig, } w := &Factory{ Factory: cmdutil.NewFactory(clientConfig), OpenShiftClientConfig: clientConfig, clients: clients, } w.Object = func() (meta.RESTMapper, runtime.ObjectTyper) { // Output using whatever version was negotiated in the client cache. The // version we decode with may not be the same as what the server requires. if cfg, err := clients.ClientConfigForVersion(nil); err == nil { cmdApiVersion := unversioned.GroupVersion{} if cfg.GroupVersion != nil { cmdApiVersion = *cfg.GroupVersion } return kubectl.OutputVersionMapper{RESTMapper: mapper, OutputVersions: []unversioned.GroupVersion{cmdApiVersion}}, api.Scheme } return mapper, api.Scheme } kClientForMapping := w.Factory.ClientForMapping w.ClientForMapping = func(mapping *meta.RESTMapping) (resource.RESTClient, error) { if latest.OriginKind(mapping.GroupVersionKind) { mappingVersion := mapping.GroupVersionKind.GroupVersion() client, err := clients.ClientForVersion(&mappingVersion) if err != nil { return nil, err } return client.RESTClient, nil } return kClientForMapping(mapping) } // Save original Describer function kDescriberFunc := w.Factory.Describer w.Describer = func(mapping *meta.RESTMapping) (kubectl.Describer, error) { if latest.OriginKind(mapping.GroupVersionKind) { oClient, kClient, err := w.Clients() if err != nil { return nil, fmt.Errorf("unable to create client %s: %v", mapping.GroupVersionKind.Kind, err) } mappingVersion := mapping.GroupVersionKind.GroupVersion() cfg, err := clients.ClientConfigForVersion(&mappingVersion) if err != nil { return nil, fmt.Errorf("unable to load a client %s: %v", mapping.GroupVersionKind.Kind, err) } describer, ok := describe.DescriberFor(mapping.GroupVersionKind.GroupKind(), oClient, kClient, cfg.Host) if !ok { return nil, fmt.Errorf("no description has been implemented for %q", mapping.GroupVersionKind.Kind) } return describer, nil } return kDescriberFunc(mapping) } kScalerFunc := w.Factory.Scaler w.Scaler = func(mapping *meta.RESTMapping) (kubectl.Scaler, error) { oc, kc, err := w.Clients() if err != nil { return nil, err } if mapping.GroupVersionKind.GroupKind() == deployapi.Kind("DeploymentConfig") { return deployscaler.NewDeploymentConfigScaler(oc, kc), nil } return kScalerFunc(mapping) } kReaperFunc := w.Factory.Reaper w.Reaper = func(mapping *meta.RESTMapping) (kubectl.Reaper, error) { oc, kc, err := w.Clients() if err != nil { return nil, err } switch mapping.GroupVersionKind.GroupKind() { case deployapi.Kind("DeploymentConfig"): return deployreaper.NewDeploymentConfigReaper(oc, kc), nil case authorizationapi.Kind("Role"): return authorizationreaper.NewRoleReaper(oc, oc), nil case authorizationapi.Kind("ClusterRole"): return authorizationreaper.NewClusterRoleReaper(oc, oc, oc), nil case userapi.Kind("User"): return authenticationreaper.NewUserReaper( client.UsersInterface(oc), client.GroupsInterface(oc), client.ClusterRoleBindingsInterface(oc), client.RoleBindingsNamespacer(oc), kclient.SecurityContextConstraintsInterface(kc), ), nil case userapi.Kind("Group"): return authenticationreaper.NewGroupReaper( client.GroupsInterface(oc), client.ClusterRoleBindingsInterface(oc), client.RoleBindingsNamespacer(oc), kclient.SecurityContextConstraintsInterface(kc), ), nil case buildapi.Kind("BuildConfig"): return buildreaper.NewBuildConfigReaper(oc), nil } return kReaperFunc(mapping) } kGenerators := w.Factory.Generators w.Generators = func(cmdName string) map[string]kubectl.Generator { originGenerators := DefaultGenerators(cmdName) kubeGenerators := kGenerators(cmdName) ret := map[string]kubectl.Generator{} for k, v := range kubeGenerators { ret[k] = v } for k, v := range originGenerators { ret[k] = v } return ret } kPodSelectorForObjectFunc := w.Factory.PodSelectorForObject w.PodSelectorForObject = func(object runtime.Object) (string, error) { switch t := object.(type) { case *deployapi.DeploymentConfig: return kubectl.MakeLabels(t.Spec.Selector), nil default: return kPodSelectorForObjectFunc(object) } } kMapBasedSelectorForObjectFunc := w.Factory.MapBasedSelectorForObject w.MapBasedSelectorForObject = func(object runtime.Object) (string, error) { switch t := object.(type) { case *deployapi.DeploymentConfig: return kubectl.MakeLabels(t.Spec.Selector), nil default: return kMapBasedSelectorForObjectFunc(object) } } kPortsForObjectFunc := w.Factory.PortsForObject w.PortsForObject = func(object runtime.Object) ([]string, error) { switch t := object.(type) { case *deployapi.DeploymentConfig: return getPorts(t.Spec.Template.Spec), nil default: return kPortsForObjectFunc(object) } } kLogsForObjectFunc := w.Factory.LogsForObject w.LogsForObject = func(object, options runtime.Object) (*kclient.Request, error) { oc, _, err := w.Clients() if err != nil { return nil, err } switch t := object.(type) { case *deployapi.DeploymentConfig: dopts, ok := options.(*deployapi.DeploymentLogOptions) if !ok { return nil, errors.New("provided options object is not a DeploymentLogOptions") } return oc.DeploymentLogs(t.Namespace).Get(t.Name, *dopts), nil case *buildapi.Build: bopts, ok := options.(*buildapi.BuildLogOptions) if !ok { return nil, errors.New("provided options object is not a BuildLogOptions") } if bopts.Version != nil { return nil, errors.New("cannot specify a version and a build") } return oc.BuildLogs(t.Namespace).Get(t.Name, *bopts), nil case *buildapi.BuildConfig: bopts, ok := options.(*buildapi.BuildLogOptions) if !ok { return nil, errors.New("provided options object is not a BuildLogOptions") } builds, err := oc.Builds(t.Namespace).List(api.ListOptions{}) if err != nil { return nil, err } builds.Items = buildapi.FilterBuilds(builds.Items, buildapi.ByBuildConfigLabelPredicate(t.Name)) if len(builds.Items) == 0 { return nil, fmt.Errorf("no builds found for %q", t.Name) } if bopts.Version != nil { // If a version has been specified, try to get the logs from that build. desired := buildutil.BuildNameForConfigVersion(t.Name, int(*bopts.Version)) return oc.BuildLogs(t.Namespace).Get(desired, *bopts), nil } sort.Sort(sort.Reverse(buildapi.BuildSliceByCreationTimestamp(builds.Items))) return oc.BuildLogs(t.Namespace).Get(builds.Items[0].Name, *bopts), nil default: return kLogsForObjectFunc(object, options) } } w.Printer = func(mapping *meta.RESTMapping, noHeaders, withNamespace, wide bool, showAll bool, showLabels, absoluteTimestamps bool, columnLabels []string) (kubectl.ResourcePrinter, error) { return describe.NewHumanReadablePrinter(noHeaders, withNamespace, wide, showAll, showLabels, absoluteTimestamps, columnLabels), nil } kCanBeExposed := w.Factory.CanBeExposed w.CanBeExposed = func(kind unversioned.GroupKind) error { if kind == deployapi.Kind("DeploymentConfig") { return nil } return kCanBeExposed(kind) } kCanBeAutoscaled := w.Factory.CanBeAutoscaled w.CanBeAutoscaled = func(kind unversioned.GroupKind) error { if kind == deployapi.Kind("DeploymentConfig") { return nil } return kCanBeAutoscaled(kind) } kAttachablePodForObjectFunc := w.Factory.AttachablePodForObject w.AttachablePodForObject = func(object runtime.Object) (*api.Pod, error) { oc, kc, err := w.Clients() if err != nil { return nil, err } switch t := object.(type) { case *deployapi.DeploymentConfig: var err error var pods *api.PodList for pods == nil || len(pods.Items) == 0 { if t.Status.LatestVersion == 0 { time.Sleep(2 * time.Second) } if t, err = oc.DeploymentConfigs(t.Namespace).Get(t.Name); err != nil { return nil, err } latestDeploymentName := deployutil.LatestDeploymentNameForConfig(t) deployment, err := kc.ReplicationControllers(t.Namespace).Get(latestDeploymentName) if err != nil { if kerrors.IsNotFound(err) { continue } return nil, err } pods, err = kc.Pods(deployment.Namespace).List(api.ListOptions{LabelSelector: labels.SelectorFromSet(deployment.Spec.Selector)}) if err != nil { return nil, err } if len(pods.Items) == 0 { time.Sleep(2 * time.Second) } } var oldestPod *api.Pod for _, pod := range pods.Items { if oldestPod == nil || pod.CreationTimestamp.Before(oldestPod.CreationTimestamp) { oldestPod = &pod } } return oldestPod, nil default: return kAttachablePodForObjectFunc(object) } } kSwaggerSchemaFunc := w.Factory.SwaggerSchema w.Factory.SwaggerSchema = func(gvk unversioned.GroupVersionKind) (*swagger.ApiDeclaration, error) { if !latest.OriginKind(gvk) { return kSwaggerSchemaFunc(gvk) } // TODO: we need to register the OpenShift API under the Kube group, and start returning the OpenShift // group from the scheme. oc, _, err := w.Clients() if err != nil { return nil, err } return w.OriginSwaggerSchema(oc.RESTClient, gvk.GroupVersion()) } w.EditorEnvs = func() []string { return []string{"OC_EDITOR", "EDITOR"} } return w }
// NewFactory creates a factory with the default Kubernetes resources defined // if optionalClientConfig is nil, then flags will be bound to a new clientcmd.ClientConfig. // if optionalClientConfig is not nil, then this factory will make use of it. func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { mapper := kubectl.ShortcutExpander{RESTMapper: api.RESTMapper} flags := pflag.NewFlagSet("", pflag.ContinueOnError) flags.SetNormalizeFunc(util.WarnWordSepNormalizeFunc) // Warn for "_" flags clientConfig := optionalClientConfig if optionalClientConfig == nil { clientConfig = DefaultClientConfig(flags) } clients := NewClientCache(clientConfig) return &Factory{ clients: clients, flags: flags, Object: func() (meta.RESTMapper, runtime.ObjectTyper) { cfg, err := clientConfig.ClientConfig() CheckErr(err) cmdApiVersion := unversioned.GroupVersion{} if cfg.GroupVersion != nil { cmdApiVersion = *cfg.GroupVersion } return kubectl.OutputVersionMapper{RESTMapper: mapper, OutputVersions: []unversioned.GroupVersion{cmdApiVersion}}, api.Scheme }, Client: func() (*client.Client, error) { return clients.ClientForVersion(nil) }, ClientConfig: func() (*client.Config, error) { return clients.ClientConfigForVersion(nil) }, RESTClient: func(mapping *meta.RESTMapping) (resource.RESTClient, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() client, err := clients.ClientForVersion(&mappingVersion) if err != nil { return nil, err } switch mapping.GroupVersionKind.Group { case api.GroupName: return client.RESTClient, nil case extensions.GroupName: return client.ExtensionsClient.RESTClient, nil } return nil, fmt.Errorf("unable to get RESTClient for resource '%s'", mapping.Resource) }, Describer: func(mapping *meta.RESTMapping) (kubectl.Describer, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() client, err := clients.ClientForVersion(&mappingVersion) if err != nil { return nil, err } if describer, ok := kubectl.DescriberFor(mapping.GroupVersionKind.GroupKind(), client); ok { return describer, nil } return nil, fmt.Errorf("no description has been implemented for %q", mapping.GroupVersionKind.Kind) }, Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll bool, absoluteTimestamps bool, columnLabels []string) (kubectl.ResourcePrinter, error) { return kubectl.NewHumanReadablePrinter(noHeaders, withNamespace, wide, showAll, absoluteTimestamps, columnLabels), nil }, PodSelectorForObject: func(object runtime.Object) (string, error) { // TODO: replace with a swagger schema based approach (identify pod selector via schema introspection) switch t := object.(type) { case *api.ReplicationController: return kubectl.MakeLabels(t.Spec.Selector), nil case *api.Pod: if len(t.Labels) == 0 { return "", fmt.Errorf("the pod has no labels and cannot be exposed") } return kubectl.MakeLabels(t.Labels), nil case *api.Service: if t.Spec.Selector == nil { return "", fmt.Errorf("the service has no pod selector set") } return kubectl.MakeLabels(t.Spec.Selector), nil default: gvk, err := api.Scheme.ObjectKind(object) if err != nil { return "", err } return "", fmt.Errorf("cannot extract pod selector from %v", gvk) } }, PortsForObject: func(object runtime.Object) ([]string, error) { // TODO: replace with a swagger schema based approach (identify pod selector via schema introspection) switch t := object.(type) { case *api.ReplicationController: return getPorts(t.Spec.Template.Spec), nil case *api.Pod: return getPorts(t.Spec), nil case *api.Service: return getServicePorts(t.Spec), nil default: gvk, err := api.Scheme.ObjectKind(object) if err != nil { return nil, err } return nil, fmt.Errorf("cannot extract ports from %v", gvk) } }, LabelsForObject: func(object runtime.Object) (map[string]string, error) { return meta.NewAccessor().Labels(object) }, LogsForObject: func(object, options runtime.Object) (*client.Request, error) { c, err := clients.ClientForVersion(nil) if err != nil { return nil, err } switch t := object.(type) { case *api.Pod: opts, ok := options.(*api.PodLogOptions) if !ok { return nil, errors.New("provided options object is not a PodLogOptions") } return c.Pods(t.Namespace).GetLogs(t.Name, opts), nil default: gvk, err := api.Scheme.ObjectKind(object) if err != nil { return nil, err } return nil, fmt.Errorf("cannot get the logs from %v", gvk) } }, Scaler: func(mapping *meta.RESTMapping) (kubectl.Scaler, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() client, err := clients.ClientForVersion(&mappingVersion) if err != nil { return nil, err } return kubectl.ScalerFor(mapping.GroupVersionKind.GroupKind(), client) }, Reaper: func(mapping *meta.RESTMapping) (kubectl.Reaper, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() client, err := clients.ClientForVersion(&mappingVersion) if err != nil { return nil, err } return kubectl.ReaperFor(mapping.GroupVersionKind.GroupKind(), client) }, Validator: func(validate bool, cacheDir string) (validation.Schema, error) { if validate { client, err := clients.ClientForVersion(nil) if err != nil { return nil, err } dir := cacheDir if len(dir) > 0 { version, err := client.ServerVersion() if err != nil { return nil, err } dir = path.Join(cacheDir, version.String()) } return &clientSwaggerSchema{ c: client, cacheDir: dir, mapper: api.RESTMapper, }, nil } return validation.NullSchema{}, nil }, SwaggerSchema: func(version unversioned.GroupVersion) (*swagger.ApiDeclaration, error) { client, err := clients.ClientForVersion(&version) if err != nil { return nil, err } return client.SwaggerSchema(version) }, DefaultNamespace: func() (string, bool, error) { return clientConfig.Namespace() }, Generators: func(cmdName string) map[string]kubectl.Generator { return DefaultGenerators(cmdName) }, CanBeExposed: func(kind unversioned.GroupKind) error { switch kind { case api.Kind("ReplicationController"), api.Kind("Service"), api.Kind("Pod"): // nothing to do here default: return fmt.Errorf("cannot expose a %s", kind) } return nil }, CanBeAutoscaled: func(kind unversioned.GroupKind) error { switch kind { case api.Kind("ReplicationController"), extensions.Kind("Deployment"): // nothing to do here default: return fmt.Errorf("cannot autoscale a %v", kind) } return nil }, AttachablePodForObject: func(object runtime.Object) (*api.Pod, error) { client, err := clients.ClientForVersion(nil) if err != nil { return nil, err } switch t := object.(type) { case *api.ReplicationController: return GetFirstPod(client, t.Namespace, t.Spec.Selector) case *extensions.Deployment: return GetFirstPod(client, t.Namespace, t.Spec.Selector) case *extensions.Job: return GetFirstPod(client, t.Namespace, t.Spec.Selector.MatchLabels) case *api.Pod: return t, nil default: gvk, err := api.Scheme.ObjectKind(object) if err != nil { return nil, err } return nil, fmt.Errorf("cannot attach to %v: not implemented", gvk) } }, EditorEnvs: func() []string { return []string{"KUBE_EDITOR", "EDITOR"} }, } }
// NewFactory creates an object that holds common methods across all OpenShift commands func NewFactory(clientConfig kclientcmd.ClientConfig) *Factory { mapper := ShortcutExpander{RESTMapper: kubectl.ShortcutExpander{RESTMapper: latest.RESTMapper}} clients := &clientCache{ clients: make(map[string]*client.Client), configs: make(map[string]*kclient.Config), loader: clientConfig, } generators := map[string]kubectl.Generator{ "route/v1": routegen.RouteGenerator{}, "run/v1": deploygen.BasicDeploymentConfigController{}, "run-controller/v1": kubectl.BasicReplicationController{}, } w := &Factory{ Factory: cmdutil.NewFactory(clientConfig), OpenShiftClientConfig: clientConfig, clients: clients, } w.Object = func() (meta.RESTMapper, runtime.ObjectTyper) { // Output using whatever version was negotiated in the client cache. The // version we decode with may not be the same as what the server requires. if cfg, err := clients.ClientConfigForVersion(""); err == nil { return kubectl.OutputVersionMapper{RESTMapper: mapper, OutputVersion: cfg.Version}, api.Scheme } return mapper, api.Scheme } kRESTClient := w.Factory.RESTClient w.RESTClient = func(mapping *meta.RESTMapping) (resource.RESTClient, error) { if latest.OriginKind(mapping.Kind, mapping.APIVersion) { client, err := clients.ClientForVersion(mapping.APIVersion) if err != nil { return nil, err } return client.RESTClient, nil } return kRESTClient(mapping) } // Save original Describer function kDescriberFunc := w.Factory.Describer w.Describer = func(mapping *meta.RESTMapping) (kubectl.Describer, error) { if latest.OriginKind(mapping.Kind, mapping.APIVersion) { oClient, kClient, err := w.Clients() if err != nil { return nil, fmt.Errorf("unable to create client %s: %v", mapping.Kind, err) } cfg, err := clients.ClientConfigForVersion(mapping.APIVersion) if err != nil { return nil, fmt.Errorf("unable to load a client %s: %v", mapping.Kind, err) } describer, ok := describe.DescriberFor(mapping.Kind, oClient, kClient, cfg.Host) if !ok { return nil, fmt.Errorf("no description has been implemented for %q", mapping.Kind) } return describer, nil } return kDescriberFunc(mapping) } kScalerFunc := w.Factory.Scaler w.Scaler = func(mapping *meta.RESTMapping) (kubectl.Scaler, error) { oc, kc, err := w.Clients() if err != nil { return nil, err } if mapping.Kind == "DeploymentConfig" { return deployscaler.NewDeploymentConfigScaler(oc, kc), nil } return kScalerFunc(mapping) } kReaperFunc := w.Factory.Reaper w.Reaper = func(mapping *meta.RESTMapping) (kubectl.Reaper, error) { oc, kc, err := w.Clients() if err != nil { return nil, err } switch mapping.Kind { case "DeploymentConfig": return deployreaper.NewDeploymentConfigReaper(oc, kc), nil case "Role": return authorizationreaper.NewRoleReaper(oc, oc), nil case "ClusterRole": return authorizationreaper.NewClusterRoleReaper(oc, oc, oc), nil case "User": return authenticationreaper.NewUserReaper( client.UsersInterface(oc), client.GroupsInterface(oc), client.ClusterRoleBindingsInterface(oc), client.RoleBindingsNamespacer(oc), kclient.SecurityContextConstraintsInterface(kc), ), nil case "Group": return authenticationreaper.NewGroupReaper( client.GroupsInterface(oc), client.ClusterRoleBindingsInterface(oc), client.RoleBindingsNamespacer(oc), kclient.SecurityContextConstraintsInterface(kc), ), nil } return kReaperFunc(mapping) } kGeneratorFunc := w.Factory.Generator w.Generator = func(name string) (kubectl.Generator, bool) { if generator, ok := generators[name]; ok { return generator, true } return kGeneratorFunc(name) } kPodSelectorForObjectFunc := w.Factory.PodSelectorForObject w.PodSelectorForObject = func(object runtime.Object) (string, error) { switch t := object.(type) { case *deployapi.DeploymentConfig: return kubectl.MakeLabels(t.Spec.Selector), nil default: return kPodSelectorForObjectFunc(object) } } kPortsForObjectFunc := w.Factory.PortsForObject w.PortsForObject = func(object runtime.Object) ([]string, error) { switch t := object.(type) { case *deployapi.DeploymentConfig: return getPorts(t.Spec.Template.Spec), nil default: return kPortsForObjectFunc(object) } } kLogsForObjectFunc := w.Factory.LogsForObject w.LogsForObject = func(object, options runtime.Object) (*kclient.Request, error) { oc, _, err := w.Clients() if err != nil { return nil, err } switch t := object.(type) { case *deployapi.DeploymentConfig: dopts, ok := options.(*deployapi.DeploymentLogOptions) if !ok { return nil, errors.New("provided options object is not a DeploymentLogOptions") } return oc.DeploymentLogs(t.Namespace).Get(t.Name, *dopts), nil case *buildapi.Build: bopts, ok := options.(*buildapi.BuildLogOptions) if !ok { return nil, errors.New("provided options object is not a BuildLogOptions") } if bopts.Version != nil { // should --version work with builds at all? return nil, errors.New("cannot specify a version and a build") } return oc.BuildLogs(t.Namespace).Get(t.Name, *bopts), nil case *buildapi.BuildConfig: bopts, ok := options.(*buildapi.BuildLogOptions) if !ok { return nil, errors.New("provided options object is not a BuildLogOptions") } builds, err := oc.Builds(t.Namespace).List(labels.Everything(), fields.Everything()) if err != nil { return nil, err } builds.Items = buildapi.FilterBuilds(builds.Items, buildapi.ByBuildConfigLabelPredicate(t.Name)) if len(builds.Items) == 0 { return nil, fmt.Errorf("no builds found for %s", t.Name) } if bopts.Version != nil { // If a version has been specified, try to get the logs from that build. desired := buildutil.BuildNameForConfigVersion(t.Name, int(*bopts.Version)) return oc.BuildLogs(t.Namespace).Get(desired, *bopts), nil } sort.Sort(sort.Reverse(buildapi.BuildSliceByCreationTimestamp(builds.Items))) return oc.BuildLogs(t.Namespace).Get(builds.Items[0].Name, *bopts), nil default: return kLogsForObjectFunc(object, options) } } w.Printer = func(mapping *meta.RESTMapping, noHeaders, withNamespace, wide bool, showAll bool, columnLabels []string) (kubectl.ResourcePrinter, error) { return describe.NewHumanReadablePrinter(noHeaders, withNamespace, wide, showAll, columnLabels), nil } kCanBeExposed := w.Factory.CanBeExposed w.CanBeExposed = func(kind string) error { if kind == "DeploymentConfig" { return nil } return kCanBeExposed(kind) } kAttachablePodForObjectFunc := w.Factory.AttachablePodForObject w.AttachablePodForObject = func(object runtime.Object) (*api.Pod, error) { oc, kc, err := w.Clients() if err != nil { return nil, err } switch t := object.(type) { case *deployapi.DeploymentConfig: var err error var pods *api.PodList for pods == nil || len(pods.Items) == 0 { if t.Status.LatestVersion == 0 { time.Sleep(2 * time.Second) } if t, err = oc.DeploymentConfigs(t.Namespace).Get(t.Name); err != nil { return nil, err } latestDeploymentName := deployutil.LatestDeploymentNameForConfig(t) deployment, err := kc.ReplicationControllers(t.Namespace).Get(latestDeploymentName) if err != nil { if kerrors.IsNotFound(err) { continue } return nil, err } pods, err = kc.Pods(deployment.Namespace).List(labels.SelectorFromSet(deployment.Spec.Selector), fields.Everything()) if err != nil { return nil, err } if len(pods.Items) == 0 { time.Sleep(2 * time.Second) } } var oldestPod *api.Pod for _, pod := range pods.Items { if oldestPod == nil || pod.CreationTimestamp.Before(oldestPod.CreationTimestamp) { oldestPod = &pod } } return oldestPod, nil default: return kAttachablePodForObjectFunc(object) } } return w }
// NewFactory creates an object that holds common methods across all OpenShift commands func NewFactory(clientConfig kclientcmd.ClientConfig) *Factory { mapper := ShortcutExpander{RESTMapper: kubectl.ShortcutExpander{RESTMapper: latest.RESTMapper}} clients := &clientCache{ clients: make(map[string]*client.Client), configs: make(map[string]*kclient.Config), loader: clientConfig, } generators := map[string]kubectl.Generator{ "route/v1": routegen.RouteGenerator{}, "run/v1": deploygen.BasicDeploymentConfigController{}, "run-controller/v1": kubectl.BasicReplicationController{}, } w := &Factory{ Factory: cmdutil.NewFactory(clientConfig), OpenShiftClientConfig: clientConfig, clients: clients, } w.Object = func() (meta.RESTMapper, runtime.ObjectTyper) { // Output using whatever version was negotiated in the client cache. The // version we decode with may not be the same as what the server requires. if cfg, err := clients.ClientConfigForVersion(""); err == nil { return kubectl.OutputVersionMapper{RESTMapper: mapper, OutputVersion: cfg.Version}, api.Scheme } return mapper, api.Scheme } kRESTClient := w.Factory.RESTClient w.RESTClient = func(mapping *meta.RESTMapping) (resource.RESTClient, error) { if latest.OriginKind(mapping.Kind, mapping.APIVersion) { client, err := clients.ClientForVersion(mapping.APIVersion) if err != nil { return nil, err } return client.RESTClient, nil } return kRESTClient(mapping) } // Save original Describer function kDescriberFunc := w.Factory.Describer w.Describer = func(mapping *meta.RESTMapping) (kubectl.Describer, error) { if latest.OriginKind(mapping.Kind, mapping.APIVersion) { oClient, kClient, err := w.Clients() if err != nil { return nil, fmt.Errorf("unable to create client %s: %v", mapping.Kind, err) } cfg, err := clients.ClientConfigForVersion(mapping.APIVersion) if err != nil { return nil, fmt.Errorf("unable to load a client %s: %v", mapping.Kind, err) } describer, ok := describe.DescriberFor(mapping.Kind, oClient, kClient, cfg.Host) if !ok { return nil, fmt.Errorf("no description has been implemented for %q", mapping.Kind) } return describer, nil } return kDescriberFunc(mapping) } kScalerFunc := w.Factory.Scaler w.Scaler = func(mapping *meta.RESTMapping) (kubectl.Scaler, error) { oc, kc, err := w.Clients() if err != nil { return nil, err } if mapping.Kind == "DeploymentConfig" { return deployscaler.NewDeploymentConfigScaler(oc, kc), nil } return kScalerFunc(mapping) } kReaperFunc := w.Factory.Reaper w.Reaper = func(mapping *meta.RESTMapping) (kubectl.Reaper, error) { oc, kc, err := w.Clients() if err != nil { return nil, err } if mapping.Kind == "DeploymentConfig" { return deployreaper.NewDeploymentConfigReaper(oc, kc), nil } return kReaperFunc(mapping) } kGeneratorFunc := w.Factory.Generator w.Generator = func(name string) (kubectl.Generator, bool) { if generator, ok := generators[name]; ok { return generator, true } return kGeneratorFunc(name) } kPodSelectorForObjectFunc := w.Factory.PodSelectorForObject w.PodSelectorForObject = func(object runtime.Object) (string, error) { switch t := object.(type) { case *deployapi.DeploymentConfig: return kubectl.MakeLabels(t.Template.ControllerTemplate.Selector), nil default: return kPodSelectorForObjectFunc(object) } } kPortsForObjectFunc := w.Factory.PortsForObject w.PortsForObject = func(object runtime.Object) ([]string, error) { switch t := object.(type) { case *deployapi.DeploymentConfig: return getPorts(t.Template.ControllerTemplate.Template.Spec), nil default: return kPortsForObjectFunc(object) } } kLogsForObjectFunc := w.Factory.LogsForObject w.LogsForObject = func(object, options runtime.Object) (*kclient.Request, error) { oc, _, err := w.Clients() if err != nil { return nil, err } switch t := object.(type) { case *deployapi.DeploymentConfig: dopts, ok := options.(*deployapi.DeploymentLogOptions) if !ok { return nil, errors.New("provided options object is not a DeploymentLogOptions") } return oc.DeploymentLogs(t.Namespace).Get(t.Name, *dopts), nil case *buildapi.Build: bopts, ok := options.(*buildapi.BuildLogOptions) if !ok { return nil, errors.New("provided options object is not a BuildLogOptions") } return oc.BuildLogs(t.Namespace).Get(t.Name, *bopts), nil case *buildapi.BuildConfig: bopts, ok := options.(*buildapi.BuildLogOptions) if !ok { return nil, errors.New("provided options object is not a BuildLogOptions") } buildsForBCSelector := labels.SelectorFromSet(map[string]string{buildapi.BuildConfigLabel: t.Name}) builds, err := oc.Builds(t.Namespace).List(buildsForBCSelector, fields.Everything()) if err != nil { return nil, err } if len(builds.Items) == 0 { return nil, fmt.Errorf("no builds found for %s", t.Name) } sort.Sort(sort.Reverse(buildapi.BuildSliceByCreationTimestamp(builds.Items))) return oc.BuildLogs(t.Namespace).Get(builds.Items[0].Name, *bopts), nil default: } return kLogsForObjectFunc(object, options) } w.Printer = func(mapping *meta.RESTMapping, noHeaders, withNamespace, wide bool, showAll bool, columnLabels []string) (kubectl.ResourcePrinter, error) { return describe.NewHumanReadablePrinter(noHeaders, withNamespace, wide, showAll, columnLabels), nil } kCanBeExposed := w.Factory.CanBeExposed w.CanBeExposed = func(kind string) error { if kind == "DeploymentConfig" { return nil } return kCanBeExposed(kind) } return w }