func (o *AppJSONOptions) Complete(f *clientcmd.Factory, cmd *cobra.Command, args []string) error { version, _ := cmd.Flags().GetString("output-version") for _, v := range strings.Split(version, ",") { gv, err := unversioned.ParseGroupVersion(v) if err != nil { return fmt.Errorf("provided output-version %q is not valid: %v", v, err) } o.OutputVersions = append(o.OutputVersions, gv) } o.OutputVersions = append(o.OutputVersions, registered.EnabledVersions()...) o.Action.Bulk.Mapper = clientcmd.ResourceMapper(f) o.Action.Bulk.Op = configcmd.Create mapper, _ := f.Object(false) o.PrintObject = cmdutil.VersionedPrintObject(f.PrintObject, cmd, mapper, o.Action.Out) o.Generator, _ = cmd.Flags().GetString("generator") ns, _, err := f.DefaultNamespace() if err != nil { return err } o.Namespace = ns o.Client, _, err = f.Clients() return err }
// Complete sets any default behavior for the command func (o *NewAppOptions) Complete(commandName string, f *clientcmd.Factory, c *cobra.Command, args []string, out io.Writer) error { o.Out = out o.ErrOut = c.Out() o.Output = kcmdutil.GetFlagString(c, "output") // Only output="" should print descriptions of intermediate steps. Everything // else should print only some specific output (json, yaml, go-template, ...) if len(o.Output) == 0 { o.Config.Out = o.Out } else { o.Config.Out = ioutil.Discard } o.Config.ErrOut = o.ErrOut o.Action.Out, o.Action.ErrOut = o.Out, o.ErrOut o.Action.Bulk.Mapper = clientcmd.ResourceMapper(f) o.Action.Bulk.Op = configcmd.Create // Retry is used to support previous versions of the API server that will // consider the presence of an unknown trigger type to be an error. o.Action.Bulk.Retry = retryBuildConfig o.Config.DryRun = o.Action.DryRun o.CommandPath = c.CommandPath() o.CommandName = commandName mapper, _ := f.Object(false) o.PrintObject = cmdutil.VersionedPrintObject(f.PrintObject, c, mapper, out) o.LogsForObject = f.LogsForObject if err := CompleteAppConfig(o.Config, f, c, args); err != nil { return err } if err := setAppConfigLabels(c, o.Config); err != nil { return err } return nil }
// Complete completes any options that are required by validate or run steps. func (opts *RegistryOptions) Complete(f *clientcmd.Factory, cmd *cobra.Command, out, errout io.Writer, args []string) error { if len(args) > 0 { return kcmdutil.UsageError(cmd, "No arguments are allowed to this command") } opts.image = opts.Config.ImageTemplate.ExpandOrDie(opts.Config.Type) opts.label = map[string]string{ "docker-registry": "default", } if opts.Config.Labels != defaultLabel { valid, remove, err := app.LabelsFromSpec(strings.Split(opts.Config.Labels, ",")) if err != nil { return err } if len(remove) > 0 { return kcmdutil.UsageError(cmd, "You may not pass negative labels in %q", opts.Config.Labels) } opts.label = valid } opts.nodeSelector = map[string]string{} if len(opts.Config.Selector) > 0 { valid, remove, err := app.LabelsFromSpec(strings.Split(opts.Config.Selector, ",")) if err != nil { return err } if len(remove) > 0 { return kcmdutil.UsageError(cmd, "You may not pass negative labels in selector %q", opts.Config.Selector) } opts.nodeSelector = valid } var portsErr error if opts.ports, portsErr = app.ContainerPortsFromString(opts.Config.Ports); portsErr != nil { return portsErr } var nsErr error if opts.namespace, _, nsErr = f.OpenShiftClientConfig.Namespace(); nsErr != nil { return fmt.Errorf("error getting namespace: %v", nsErr) } _, _, kClient, kClientErr := f.Clients() if kClientErr != nil { return fmt.Errorf("error getting client: %v", kClientErr) } opts.serviceClient = kClient.Core() opts.Config.Action.Bulk.Mapper = clientcmd.ResourceMapper(f) opts.Config.Action.Out, opts.Config.Action.ErrOut = out, errout opts.Config.Action.Bulk.Op = configcmd.Create opts.out = out opts.cmd = cmd opts.factory = f return nil }
// Run runs the ipfailover command. func Run(f *clientcmd.Factory, options *ipfailover.IPFailoverConfigCmdOptions, cmd *cobra.Command, args []string) error { name, err := getConfigurationName(args) if err != nil { return err } if len(options.ServiceAccount) == 0 { return fmt.Errorf("you must specify a service account for the ipfailover pod with --service-account, it cannot be blank") } options.Action.Bulk.Mapper = clientcmd.ResourceMapper(f) options.Action.Bulk.Op = configcmd.Create if err := ipfailover.ValidateCmdOptions(options); err != nil { return err } p, err := getPlugin(name, f, options) if err != nil { return err } list, err := p.Generate() if err != nil { return err } namespace, _, err := f.DefaultNamespace() if err != nil { return err } _, kClient, _, err := f.Clients() if err != nil { return fmt.Errorf("error getting client: %v", err) } if err := validateServiceAccount(kClient, namespace, options.ServiceAccount); err != nil { return fmt.Errorf("ipfailover could not be created; %v", err) } configList := []runtime.Object{ &kapi.ServiceAccount{ObjectMeta: kapi.ObjectMeta{Name: options.ServiceAccount}}, } list.Items = append(configList, list.Items...) if options.Action.ShouldPrint() { mapper, _ := f.Object(false) return cmdutil.VersionedPrintObject(f.PrintObject, cmd, mapper, options.Action.Out)(list) } if errs := options.Action.WithMessage(fmt.Sprintf("Creating IP failover %s", name), "created").Run(list, namespace); len(errs) > 0 { return cmdutil.ErrExit } return nil }
// Complete sets any default behavior for the command func (o *NewBuildOptions) Complete(baseName, commandName string, f *clientcmd.Factory, c *cobra.Command, args []string, out, errout io.Writer, in io.Reader) error { o.In = in o.Out = out o.ErrOut = errout o.Output = kcmdutil.GetFlagString(c, "output") // Only output="" should print descriptions of intermediate steps. Everything // else should print only some specific output (json, yaml, go-template, ...) o.Config.In = in if len(o.Output) == 0 { o.Config.Out = o.Out } else { o.Config.Out = ioutil.Discard } o.Config.ErrOut = o.ErrOut o.Action.Out, o.Action.ErrOut = o.Out, o.ErrOut o.Action.Bulk.Mapper = clientcmd.ResourceMapper(f) o.Action.Bulk.Op = configcmd.Create // Retry is used to support previous versions of the API server that will // consider the presence of an unknown trigger type to be an error. o.Action.Bulk.Retry = retryBuildConfig o.Config.DryRun = o.Action.DryRun o.Config.AllowNonNumericExposedPorts = true o.BaseName = baseName o.CommandPath = c.CommandPath() o.CommandName = commandName cmdutil.WarnAboutCommaSeparation(o.ErrOut, o.Config.Environment, "--env") mapper, _ := f.Object(false) o.PrintObject = cmdutil.VersionedPrintObject(f.PrintObject, c, mapper, out) o.LogsForObject = f.LogsForObject if err := CompleteAppConfig(o.Config, f, c, args); err != nil { return err } if o.Config.Dockerfile == "-" { data, err := ioutil.ReadAll(in) if err != nil { return err } o.Config.Dockerfile = string(data) } if err := setAppConfigLabels(c, o.Config); err != nil { return err } return nil }
// Run runs the ipfailover command. func Run(f *clientcmd.Factory, options *ipfailover.IPFailoverConfigCmdOptions, cmd *cobra.Command, args []string) error { name, err := getConfigurationName(args) if err != nil { return err } options.Action.Bulk.Mapper = clientcmd.ResourceMapper(f) options.Action.Bulk.Op = configcmd.Create if err := ipfailover.ValidateCmdOptions(options); err != nil { return err } p, err := getPlugin(name, f, options) if err != nil { return err } list, err := p.Generate() if err != nil { return err } if options.Action.ShouldPrint() { mapper, _ := f.Object(false) return cmdutil.VersionedPrintObject(f.PrintObject, cmd, mapper, options.Action.Out)(list) } namespace, _, err := f.DefaultNamespace() if err != nil { return err } if errs := options.Action.WithMessage(fmt.Sprintf("Creating IP failover %s", name), "created").Run(list, namespace); len(errs) > 0 { return cmdutil.ErrExit } return nil }
// InstallLogging checks whether logging is installed and installs it if not already installed func (h *Helper) InstallLogging(f *clientcmd.Factory, publicHostname, loggerHost, imagePrefix, imageVersion string) error { osClient, _, kubeClient, err := f.Clients() if err != nil { return errors.NewError("cannot obtain API clients").WithCause(err).WithDetails(h.OriginLog()) } _, err = kubeClient.Core().Namespaces().Get(loggingNamespace) if err == nil { // If there's no error, the logging namespace already exists and we won't initialize it return nil } // Create logging namespace out := &bytes.Buffer{} err = CreateProject(f, loggingNamespace, "", "", "oc", out) if err != nil { return errors.NewError("cannot create logging project").WithCause(err).WithDetails(out.String()) } // Instantiate logging deployer account template err = instantiateTemplate(osClient, clientcmd.ResourceMapper(f), "openshift", loggingDeployerAccountTemplate, loggingNamespace, nil) if err != nil { return errors.NewError("cannot instantiate logger accounts").WithCause(err) } // Add oauth-editor cluster role to logging-deployer sa if err = AddClusterRole(osClient, "oauth-editor", "system:serviceaccount:logging:logging-deployer"); err != nil { return errors.NewError("cannot add oauth editor role to logging deployer service account").WithCause(err).WithDetails(h.OriginLog()) } // Add cluster-reader cluster role to aggregated-logging-fluentd sa if err = AddClusterRole(osClient, "cluster-reader", "system:serviceaccount:logging:aggregated-logging-fluentd"); err != nil { return errors.NewError("cannot cluster reader role to logging fluentd service account").WithCause(err).WithDetails(h.OriginLog()) } // Add privileged SCC to aggregated-logging-fluentd sa if err = AddSCCToServiceAccount(kubeClient, "privileged", "aggregated-logging-fluentd", loggingNamespace); err != nil { return errors.NewError("cannot add privileged security context constraint to logging fluentd service account").WithCause(err).WithDetails(h.OriginLog()) } // Label all nodes with default fluentd label nodeList, err := kubeClient.Core().Nodes().List(kapi.ListOptions{}) if err != nil { return errors.NewError("cannot retrieve nodes").WithCause(err).WithDetails(h.OriginLog()) } // Iterate through all nodes (there should only be one) for _, node := range nodeList.Items { node.Labels["logging-infra-fluentd"] = "true" if _, err = kubeClient.Core().Nodes().Update(&node); err != nil { return errors.NewError("cannot update labels on node %s", node.Name).WithCause(err) } } // Create ConfigMap with deployment values loggingConfig := &kapi.ConfigMap{} loggingConfig.Name = "logging-deployer" loggingConfig.Data = map[string]string{ "kibana-hostname": loggerHost, "public-master-url": fmt.Sprintf("https://%s:8443", publicHostname), "es-cluster-size": "1", "es-instance-ram": "1024M", } kubeClient.Core().ConfigMaps(loggingNamespace).Create(loggingConfig) // Instantiate logging deployer deployerParams := map[string]string{ "IMAGE_VERSION": imageVersion, "IMAGE_PREFIX": fmt.Sprintf("%s-", imagePrefix), "MODE": "install", } err = instantiateTemplate(osClient, clientcmd.ResourceMapper(f), "openshift", loggingDeployerTemplate, loggingNamespace, deployerParams) if err != nil { return errors.NewError("cannot instantiate logging deployer").WithCause(err) } return nil }
// RunCmdRegistry contains all the necessary functionality for the OpenShift cli registry command func RunCmdRegistry(f *clientcmd.Factory, cmd *cobra.Command, out io.Writer, cfg *RegistryConfig, args []string) error { var name string switch len(args) { case 0: name = "docker-registry" default: return kcmdutil.UsageError(cmd, "No arguments are allowed to this command") } ports, err := app.ContainerPortsFromString(cfg.Ports) if err != nil { return err } label := map[string]string{ "docker-registry": "default", } if cfg.Labels != defaultLabel { valid, remove, err := app.LabelsFromSpec(strings.Split(cfg.Labels, ",")) if err != nil { return err } if len(remove) > 0 { return kcmdutil.UsageError(cmd, "You may not pass negative labels in %q", cfg.Labels) } label = valid } nodeSelector := map[string]string{} if len(cfg.Selector) > 0 { valid, remove, err := app.LabelsFromSpec(strings.Split(cfg.Selector, ",")) if err != nil { return err } if len(remove) > 0 { return kcmdutil.UsageError(cmd, "You may not pass negative labels in selector %q", cfg.Selector) } nodeSelector = valid } image := cfg.ImageTemplate.ExpandOrDie(cfg.Type) namespace, _, err := f.OpenShiftClientConfig.Namespace() if err != nil { return fmt.Errorf("error getting client: %v", err) } _, kClient, err := f.Clients() if err != nil { return fmt.Errorf("error getting client: %v", err) } cfg.Action.Bulk.Mapper = clientcmd.ResourceMapper(f) cfg.Action.Out, cfg.Action.ErrOut = out, cmd.Out() cfg.Action.Bulk.Op = configcmd.Create var clusterIP string output := cfg.Action.ShouldPrint() generate := output if !generate { service, err := kClient.Services(namespace).Get(name) if err != nil { if !errors.IsNotFound(err) && !generate { return fmt.Errorf("can't check for existing docker-registry %q: %v", name, err) } if !output && cfg.Action.DryRun { return fmt.Errorf("Docker registry %q service does not exist", name) } generate = true } else { clusterIP = service.Spec.ClusterIP } } if !generate { fmt.Fprintf(out, "Docker registry %q service exists\n", name) return nil } // create new registry secretEnv := app.Environment{} switch { case len(cfg.ServiceAccount) == 0 && len(cfg.Credentials) == 0: return fmt.Errorf("registry could not be created; a service account or the path to a .kubeconfig file must be provided") case len(cfg.Credentials) > 0: clientConfigLoadingRules := &kclientcmd.ClientConfigLoadingRules{ExplicitPath: cfg.Credentials} credentials, err := clientConfigLoadingRules.Load() if err != nil { return fmt.Errorf("registry does not exist; the provided credentials %q could not be loaded: %v", cfg.Credentials, err) } config, err := kclientcmd.NewDefaultClientConfig(*credentials, &kclientcmd.ConfigOverrides{}).ClientConfig() if err != nil { return fmt.Errorf("registry does not exist; the provided credentials %q could not be used: %v", cfg.Credentials, err) } if err := restclient.LoadTLSFiles(config); err != nil { return fmt.Errorf("registry does not exist; the provided credentials %q could not load certificate info: %v", cfg.Credentials, err) } insecure := "false" if config.Insecure { insecure = "true" } else { if len(config.KeyData) == 0 || len(config.CertData) == 0 { return fmt.Errorf("registry does not exist; the provided credentials %q are missing the client certificate and/or key", cfg.Credentials) } } secretEnv = app.Environment{ "OPENSHIFT_MASTER": config.Host, "OPENSHIFT_CA_DATA": string(config.CAData), "OPENSHIFT_KEY_DATA": string(config.KeyData), "OPENSHIFT_CERT_DATA": string(config.CertData), "OPENSHIFT_INSECURE": insecure, } } needServiceAccountRole := len(cfg.ServiceAccount) > 0 && len(cfg.Credentials) == 0 var servingCert, servingKey []byte if len(cfg.ServingCertPath) > 0 { data, err := ioutil.ReadFile(cfg.ServingCertPath) if err != nil { return fmt.Errorf("registry does not exist; could not load TLS certificate file %q: %v", cfg.ServingCertPath, err) } servingCert = data } if len(cfg.ServingKeyPath) > 0 { data, err := ioutil.ReadFile(cfg.ServingKeyPath) if err != nil { return fmt.Errorf("registry does not exist; could not load TLS private key file %q: %v", cfg.ServingKeyPath, err) } servingCert = data } env := app.Environment{} env.Add(secretEnv) healthzPort := defaultPort if len(ports) > 0 { healthzPort = ports[0].ContainerPort env["REGISTRY_HTTP_ADDR"] = fmt.Sprintf(":%d", healthzPort) env["REGISTRY_HTTP_NET"] = "tcp" } secrets, volumes, mounts, extraEnv, tls, err := generateSecretsConfig(cfg, namespace, servingCert, servingKey) if err != nil { return err } env.Add(extraEnv) livenessProbe := generateLivenessProbeConfig(healthzPort, tls) readinessProbe := generateReadinessProbeConfig(healthzPort, tls) mountHost := len(cfg.HostMount) > 0 podTemplate := &kapi.PodTemplateSpec{ ObjectMeta: kapi.ObjectMeta{Labels: label}, Spec: kapi.PodSpec{ NodeSelector: nodeSelector, Containers: []kapi.Container{ { Name: "registry", Image: image, Ports: ports, Env: env.List(), VolumeMounts: append(mounts, kapi.VolumeMount{ Name: "registry-storage", MountPath: cfg.Volume, }), SecurityContext: &kapi.SecurityContext{ Privileged: &mountHost, }, LivenessProbe: livenessProbe, ReadinessProbe: readinessProbe, }, }, Volumes: append(volumes, kapi.Volume{ Name: "registry-storage", VolumeSource: kapi.VolumeSource{}, }), ServiceAccountName: cfg.ServiceAccount, }, } if mountHost { podTemplate.Spec.Volumes[len(podTemplate.Spec.Volumes)-1].HostPath = &kapi.HostPathVolumeSource{Path: cfg.HostMount} } else { podTemplate.Spec.Volumes[len(podTemplate.Spec.Volumes)-1].EmptyDir = &kapi.EmptyDirVolumeSource{} } objects := []runtime.Object{} for _, s := range secrets { objects = append(objects, s) } if needServiceAccountRole { objects = append(objects, &kapi.ServiceAccount{ObjectMeta: kapi.ObjectMeta{Name: cfg.ServiceAccount}}, &authapi.ClusterRoleBinding{ ObjectMeta: kapi.ObjectMeta{Name: fmt.Sprintf("registry-%s-role", cfg.Name)}, Subjects: []kapi.ObjectReference{ { Kind: "ServiceAccount", Name: cfg.ServiceAccount, Namespace: namespace, }, }, RoleRef: kapi.ObjectReference{ Kind: "ClusterRole", Name: "system:registry", }, }, ) } if cfg.DaemonSet { objects = append(objects, &extensions.DaemonSet{ ObjectMeta: kapi.ObjectMeta{ Name: name, Labels: label, }, Spec: extensions.DaemonSetSpec{ Template: kapi.PodTemplateSpec{ ObjectMeta: podTemplate.ObjectMeta, Spec: podTemplate.Spec, }, }, }) } else { objects = append(objects, &deployapi.DeploymentConfig{ ObjectMeta: kapi.ObjectMeta{ Name: name, Labels: label, }, Spec: deployapi.DeploymentConfigSpec{ Replicas: cfg.Replicas, Selector: label, Triggers: []deployapi.DeploymentTriggerPolicy{ {Type: deployapi.DeploymentTriggerOnConfigChange}, }, Template: podTemplate, }, }) } objects = app.AddServices(objects, true) // Set registry service's sessionAffinity to ClientIP to prevent push // failures due to a use of poorly consistent storage shared by // multiple replicas. Also reuse the cluster IP if provided to avoid // changing the internal value. for _, obj := range objects { switch t := obj.(type) { case *kapi.Service: t.Spec.SessionAffinity = kapi.ServiceAffinityClientIP t.Spec.ClusterIP = clusterIP } } // TODO: label all created objects with the same label list := &kapi.List{Items: objects} if cfg.Action.ShouldPrint() { mapper, _ := f.Object(false) fn := cmdutil.VersionedPrintObject(f.PrintObject, cmd, mapper, out) if err := fn(list); err != nil { return fmt.Errorf("unable to print object: %v", err) } return nil } if errs := cfg.Action.WithMessage(fmt.Sprintf("Creating registry %s", cfg.Name), "created").Run(list, namespace); len(errs) > 0 { return cmdutil.ErrExit } return nil }
// RunCmdRouter contains all the necessary functionality for the // OpenShift CLI router command. func RunCmdRouter(f *clientcmd.Factory, cmd *cobra.Command, out, errout io.Writer, cfg *RouterConfig, args []string) error { switch len(args) { case 0: // uses default value case 1: cfg.Name = args[0] default: return kcmdutil.UsageError(cmd, "You may pass zero or one arguments to provide a name for the router") } name := cfg.Name var defaultOutputErr error if len(cfg.StatsUsername) > 0 { if strings.Contains(cfg.StatsUsername, ":") { return kcmdutil.UsageError(cmd, "username %s must not contain ':'", cfg.StatsUsername) } } if len(cfg.Subdomain) > 0 && len(cfg.ForceSubdomain) > 0 { return kcmdutil.UsageError(cmd, "only one of --subdomain, --force-subdomain can be specified") } ports, err := app.ContainerPortsFromString(cfg.Ports) if err != nil { return fmt.Errorf("unable to parse --ports: %v", err) } // HostNetwork overrides HostPorts if cfg.HostNetwork { cfg.HostPorts = false } // For the host networking case, ensure the ports match. if cfg.HostNetwork { for i := 0; i < len(ports); i++ { if ports[i].HostPort != 0 && ports[i].ContainerPort != ports[i].HostPort { return fmt.Errorf("when using host networking mode, container port %d and host port %d must be equal", ports[i].ContainerPort, ports[i].HostPort) } } } if cfg.StatsPort > 0 { port := kapi.ContainerPort{ Name: "stats", ContainerPort: int32(cfg.StatsPort), Protocol: kapi.ProtocolTCP, } if cfg.HostPorts { port.HostPort = int32(cfg.StatsPort) } ports = append(ports, port) } label := map[string]string{"router": name} if cfg.Labels != defaultLabel { valid, remove, err := app.LabelsFromSpec(strings.Split(cfg.Labels, ",")) if err != nil { glog.Fatal(err) } if len(remove) > 0 { return kcmdutil.UsageError(cmd, "You may not pass negative labels in %q", cfg.Labels) } label = valid } nodeSelector := map[string]string{} if len(cfg.Selector) > 0 { valid, remove, err := app.LabelsFromSpec(strings.Split(cfg.Selector, ",")) if err != nil { glog.Fatal(err) } if len(remove) > 0 { return kcmdutil.UsageError(cmd, "You may not pass negative labels in selector %q", cfg.Selector) } nodeSelector = valid } image := cfg.ImageTemplate.ExpandOrDie(cfg.Type) namespace, _, err := f.OpenShiftClientConfig.Namespace() if err != nil { return fmt.Errorf("error getting client: %v", err) } _, kClient, _, err := f.Clients() if err != nil { return fmt.Errorf("error getting client: %v", err) } cfg.Action.Bulk.Mapper = clientcmd.ResourceMapper(f) cfg.Action.Out, cfg.Action.ErrOut = out, errout cfg.Action.Bulk.Op = configcmd.Create var clusterIP string output := cfg.Action.ShouldPrint() generate := output service, err := kClient.Services(namespace).Get(name) if err != nil { if !generate { if !errors.IsNotFound(err) { return fmt.Errorf("can't check for existing router %q: %v", name, err) } if !output && cfg.Action.DryRun { return fmt.Errorf("Router %q service does not exist", name) } generate = true } } else { clusterIP = service.Spec.ClusterIP } if !generate { fmt.Fprintf(out, "Router %q service exists\n", name) return nil } if len(cfg.ServiceAccount) == 0 { return fmt.Errorf("you must specify a service account for the router with --service-account") } if err := validateServiceAccount(kClient, namespace, cfg.ServiceAccount, cfg.HostNetwork, cfg.HostPorts); err != nil { err = fmt.Errorf("router could not be created; %v", err) if !cfg.Action.ShouldPrint() { return err } fmt.Fprintf(errout, "error: %v\n", err) defaultOutputErr = cmdutil.ErrExit } // create new router secretEnv := app.Environment{} switch { case len(cfg.Credentials) == 0 && len(cfg.ServiceAccount) == 0: return fmt.Errorf("router could not be created; you must specify a service account with --service-account, or a .kubeconfig file path containing credentials for connecting the router to the master with --credentials") case len(cfg.Credentials) > 0: clientConfigLoadingRules := &kclientcmd.ClientConfigLoadingRules{ExplicitPath: cfg.Credentials, Precedence: []string{}} credentials, err := clientConfigLoadingRules.Load() if err != nil { return fmt.Errorf("router could not be created; the provided credentials %q could not be loaded: %v", cfg.Credentials, err) } config, err := kclientcmd.NewDefaultClientConfig(*credentials, &kclientcmd.ConfigOverrides{}).ClientConfig() if err != nil { return fmt.Errorf("router could not be created; the provided credentials %q could not be used: %v", cfg.Credentials, err) } if err := restclient.LoadTLSFiles(config); err != nil { return fmt.Errorf("router could not be created; the provided credentials %q could not load certificate info: %v", cfg.Credentials, err) } insecure := "false" if config.Insecure { insecure = "true" } secretEnv.Add(app.Environment{ "OPENSHIFT_MASTER": config.Host, "OPENSHIFT_CA_DATA": string(config.CAData), "OPENSHIFT_KEY_DATA": string(config.KeyData), "OPENSHIFT_CERT_DATA": string(config.CertData), "OPENSHIFT_INSECURE": insecure, }) } createServiceAccount := len(cfg.ServiceAccount) > 0 && len(cfg.Credentials) == 0 defaultCert, err := fileutil.LoadData(cfg.DefaultCertificate) if err != nil { return fmt.Errorf("router could not be created; error reading default certificate file: %v", err) } if len(cfg.StatsPassword) == 0 { cfg.StatsPassword = generateStatsPassword() if !cfg.Action.ShouldPrint() { fmt.Fprintf(errout, "info: password for stats user %s has been set to %s\n", cfg.StatsUsername, cfg.StatsPassword) } } env := app.Environment{ "ROUTER_SUBDOMAIN": cfg.Subdomain, "ROUTER_SERVICE_NAME": name, "ROUTER_SERVICE_NAMESPACE": namespace, "ROUTER_SERVICE_HTTP_PORT": "80", "ROUTER_SERVICE_HTTPS_PORT": "443", "ROUTER_EXTERNAL_HOST_HOSTNAME": cfg.ExternalHost, "ROUTER_EXTERNAL_HOST_USERNAME": cfg.ExternalHostUsername, "ROUTER_EXTERNAL_HOST_PASSWORD": cfg.ExternalHostPassword, "ROUTER_EXTERNAL_HOST_HTTP_VSERVER": cfg.ExternalHostHttpVserver, "ROUTER_EXTERNAL_HOST_HTTPS_VSERVER": cfg.ExternalHostHttpsVserver, "ROUTER_EXTERNAL_HOST_INSECURE": strconv.FormatBool(cfg.ExternalHostInsecure), "ROUTER_EXTERNAL_HOST_PARTITION_PATH": cfg.ExternalHostPartitionPath, "ROUTER_EXTERNAL_HOST_PRIVKEY": privkeyPath, "ROUTER_EXTERNAL_HOST_INTERNAL_ADDRESS": cfg.ExternalHostInternalIP, "ROUTER_EXTERNAL_HOST_VXLAN_GW_CIDR": cfg.ExternalHostVxLANGateway, "STATS_PORT": strconv.Itoa(cfg.StatsPort), "STATS_USERNAME": cfg.StatsUsername, "STATS_PASSWORD": cfg.StatsPassword, } if len(cfg.ForceSubdomain) > 0 { env["ROUTER_SUBDOMAIN"] = cfg.ForceSubdomain env["ROUTER_OVERRIDE_HOSTNAME"] = "true" } env.Add(secretEnv) if len(defaultCert) > 0 { if cfg.SecretsAsEnv { env.Add(app.Environment{"DEFAULT_CERTIFICATE": string(defaultCert)}) } else { env.Add(app.Environment{"DEFAULT_CERTIFICATE_PATH": defaultCertificatePath}) } } env.Add(app.Environment{"DEFAULT_CERTIFICATE_DIR": defaultCertificateDir}) var certName = fmt.Sprintf("%s-certs", cfg.Name) secrets, volumes, mounts, err := generateSecretsConfig(cfg, kClient, namespace, defaultCert, certName) if err != nil { return fmt.Errorf("router could not be created: %v", err) } livenessProbe := generateLivenessProbeConfig(cfg, ports) readinessProbe := generateReadinessProbeConfig(cfg, ports) exposedPorts := make([]kapi.ContainerPort, len(ports)) copy(exposedPorts, ports) if !cfg.HostPorts { for i := range exposedPorts { exposedPorts[i].HostPort = 0 } } containers := []kapi.Container{ { Name: "router", Image: image, Ports: exposedPorts, Env: env.List(), LivenessProbe: livenessProbe, ReadinessProbe: readinessProbe, ImagePullPolicy: kapi.PullIfNotPresent, VolumeMounts: mounts, Resources: kapi.ResourceRequirements{ Requests: kapi.ResourceList{ kapi.ResourceCPU: resource.MustParse("100m"), kapi.ResourceMemory: resource.MustParse("256Mi"), }, }, }, } if cfg.StatsPort > 0 && cfg.ExposeMetrics { pc := generateMetricsExporterContainer(cfg, env) if pc != nil { containers = append(containers, *pc) } } objects := []runtime.Object{} for _, s := range secrets { objects = append(objects, s) } if createServiceAccount { objects = append(objects, &kapi.ServiceAccount{ObjectMeta: kapi.ObjectMeta{Name: cfg.ServiceAccount}}, &authapi.ClusterRoleBinding{ ObjectMeta: kapi.ObjectMeta{Name: generateRoleBindingName(cfg.Name)}, Subjects: []kapi.ObjectReference{ { Kind: "ServiceAccount", Name: cfg.ServiceAccount, Namespace: namespace, }, }, RoleRef: kapi.ObjectReference{ Kind: "ClusterRole", Name: "system:router", }, }, ) } objects = append(objects, &deployapi.DeploymentConfig{ ObjectMeta: kapi.ObjectMeta{ Name: name, Labels: label, }, Spec: deployapi.DeploymentConfigSpec{ Strategy: deployapi.DeploymentStrategy{ Type: deployapi.DeploymentStrategyTypeRolling, RollingParams: &deployapi.RollingDeploymentStrategyParams{MaxUnavailable: intstr.FromString("25%")}, }, Replicas: cfg.Replicas, Selector: label, Triggers: []deployapi.DeploymentTriggerPolicy{ {Type: deployapi.DeploymentTriggerOnConfigChange}, }, Template: &kapi.PodTemplateSpec{ ObjectMeta: kapi.ObjectMeta{Labels: label}, Spec: kapi.PodSpec{ SecurityContext: &kapi.PodSecurityContext{ HostNetwork: cfg.HostNetwork, }, ServiceAccountName: cfg.ServiceAccount, NodeSelector: nodeSelector, Containers: containers, Volumes: volumes, }, }, }, }) objects = app.AddServices(objects, false) // set the service port to the provided output port value for i := range objects { switch t := objects[i].(type) { case *kapi.Service: t.Spec.ClusterIP = clusterIP for j, servicePort := range t.Spec.Ports { for _, targetPort := range ports { if targetPort.ContainerPort == servicePort.Port && targetPort.HostPort != 0 { t.Spec.Ports[j].Port = targetPort.HostPort } } } if len(defaultCert) == 0 { // When a user does not provide the default cert (pem), create one via a Service annotation // The secret generated by the service annotaion contains a tls.crt and tls.key // which ultimately need to be combined into a pem t.Annotations = map[string]string{"service.alpha.openshift.io/serving-cert-secret-name": certName} } } } // TODO: label all created objects with the same label - router=<name> list := &kapi.List{Items: objects} if cfg.Action.ShouldPrint() { mapper, _ := f.Object(false) fn := cmdutil.VersionedPrintObject(f.PrintObject, cmd, mapper, out) if err := fn(list); err != nil { return fmt.Errorf("unable to print object: %v", err) } return defaultOutputErr } levelPrefixFilter := func(e error) string { // only ignore SA/RB errors if we were creating the service account if createServiceAccount && ignoreError(e, cfg.ServiceAccount, generateRoleBindingName(cfg.Name)) { return "warning" } return "error" } cfg.Action.Bulk.IgnoreError = func(e error) bool { return levelPrefixFilter(e) == "warning" } if errs := cfg.Action.WithMessageAndPrefix(fmt.Sprintf("Creating router %s", cfg.Name), "created", levelPrefixFilter).Run(list, namespace); len(errs) > 0 { return cmdutil.ErrExit } return nil }