// Create processes a Template and creates a new list of objects func (s *REST) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, error) { tpl, ok := obj.(*api.Template) if !ok { return nil, errors.NewBadRequest("not a template") } if errs := templatevalidation.ValidateProcessedTemplate(tpl); len(errs) > 0 { return nil, errors.NewInvalid(api.Kind("Template"), tpl.Name, errs) } generators := map[string]generator.Generator{ "expression": generator.NewExpressionValueGenerator(rand.New(rand.NewSource(time.Now().UnixNano()))), } processor := template.NewProcessor(generators) if errs := processor.Process(tpl); len(errs) > 0 { glog.V(1).Infof(errs.ToAggregate().Error()) return nil, errors.NewInvalid(api.Kind("Template"), tpl.Name, errs) } // we know that we get back runtime.Unstructured objects from the Process call. We need to encode those // objects using the unstructured codec BEFORE the REST layers gets its shot at encoding to avoid a layered // encode being done. for i := range tpl.Objects { tpl.Objects[i] = runtime.NewEncodable(runtime.UnstructuredJSONScheme, tpl.Objects[i]) } return tpl, nil }
func describerMap(c *client.Client, kclient kclient.Interface, host string) map[unversioned.GroupKind]kctl.Describer { m := map[unversioned.GroupKind]kctl.Describer{ buildapi.Kind("Build"): &BuildDescriber{c, kclient}, buildapi.Kind("BuildConfig"): &BuildConfigDescriber{c, host}, deployapi.Kind("DeploymentConfig"): &DeploymentConfigDescriber{c, kclient, nil}, authorizationapi.Kind("Identity"): &IdentityDescriber{c}, imageapi.Kind("Image"): &ImageDescriber{c}, imageapi.Kind("ImageStream"): &ImageStreamDescriber{c}, imageapi.Kind("ImageStreamTag"): &ImageStreamTagDescriber{c}, imageapi.Kind("ImageStreamImage"): &ImageStreamImageDescriber{c}, routeapi.Kind("Route"): &RouteDescriber{c, kclient}, projectapi.Kind("Project"): &ProjectDescriber{c, kclient}, templateapi.Kind("Template"): &TemplateDescriber{c, meta.NewAccessor(), kapi.Scheme, nil}, authorizationapi.Kind("Policy"): &PolicyDescriber{c}, authorizationapi.Kind("PolicyBinding"): &PolicyBindingDescriber{c}, authorizationapi.Kind("RoleBinding"): &RoleBindingDescriber{c}, authorizationapi.Kind("Role"): &RoleDescriber{c}, authorizationapi.Kind("ClusterPolicy"): &ClusterPolicyDescriber{c}, authorizationapi.Kind("ClusterPolicyBinding"): &ClusterPolicyBindingDescriber{c}, authorizationapi.Kind("ClusterRoleBinding"): &ClusterRoleBindingDescriber{c}, authorizationapi.Kind("ClusterRole"): &ClusterRoleDescriber{c}, oauthapi.Kind("OAuthAccessToken"): &OAuthAccessTokenDescriber{c}, userapi.Kind("User"): &UserDescriber{c}, userapi.Kind("Group"): &GroupDescriber{c.Groups()}, userapi.Kind("UserIdentityMapping"): &UserIdentityMappingDescriber{c}, quotaapi.Kind("ClusterResourceQuota"): &ClusterQuotaDescriber{c}, quotaapi.Kind("AppliedClusterResourceQuota"): &AppliedClusterQuotaDescriber{c}, } return m }
// RunProject contains all the necessary functionality for the OpenShift cli process command func RunProcess(f *clientcmd.Factory, out io.Writer, cmd *cobra.Command, args []string) error { templateName, valueArgs := "", []string{} for _, s := range args { isValue := strings.Contains(s, "=") switch { case isValue: valueArgs = append(valueArgs, s) case !isValue && len(templateName) == 0: templateName = s case !isValue && len(templateName) > 0: return kcmdutil.UsageError(cmd, "template name must be specified only once: %s", s) } } filename := kcmdutil.GetFlagString(cmd, "filename") if len(templateName) == 0 && len(filename) == 0 { return kcmdutil.UsageError(cmd, "Must pass a filename or name of stored template") } if kcmdutil.GetFlagBool(cmd, "parameters") { for _, flag := range []string{"value", "labels", "output", "output-version", "raw", "template"} { if f := cmd.Flags().Lookup(flag); f != nil && f.Changed { return kcmdutil.UsageError(cmd, "The --parameters flag does not process the template, can't be used with --%v", flag) } } } namespace, explicit, err := f.DefaultNamespace() if err != nil { return err } mapper, typer := f.Object() client, _, err := f.Clients() if err != nil { return err } var ( objects []runtime.Object infos []*resource.Info ) mapping, err := mapper.RESTMapping(templateapi.Kind("Template")) if err != nil { return err } // When templateName is not empty, then we fetch the template from the // server, otherwise we require to set the `-f` parameter. if len(templateName) > 0 { var ( storedTemplate, rs string sourceNamespace string ok bool ) sourceNamespace, rs, storedTemplate, ok = parseNamespaceResourceName(templateName, namespace) if !ok { return fmt.Errorf("invalid argument %q", templateName) } if len(rs) > 0 && (rs != "template" && rs != "templates") { return fmt.Errorf("unable to process invalid resource %q", rs) } if len(storedTemplate) == 0 { return fmt.Errorf("invalid value syntax %q", templateName) } templateObj, err := client.Templates(sourceNamespace).Get(storedTemplate) if err != nil { if errors.IsNotFound(err) { return fmt.Errorf("template %q could not be found", storedTemplate) } return err } templateObj.CreationTimestamp = unversioned.Now() infos = append(infos, &resource.Info{Object: templateObj}) } else { infos, err = resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), kapi.Codecs.UniversalDecoder()). NamespaceParam(namespace).RequireNamespace(). FilenameParam(explicit, filename). Do(). Infos() if err != nil { return err } } outputFormat := kcmdutil.GetFlagString(cmd, "output") for i := range infos { obj, ok := infos[i].Object.(*templateapi.Template) if !ok { sourceName := filename if len(templateName) > 0 { sourceName = namespace + "/" + templateName } fmt.Fprintf(cmd.Out(), "unable to parse %q, not a valid Template but %s\n", sourceName, reflect.TypeOf(infos[i].Object)) continue } // If 'parameters' flag is set it does not do processing but only print // the template parameters to console for inspection. // If multiple templates are passed, this will print combined output for all // templates. if kcmdutil.GetFlagBool(cmd, "parameters") { if len(infos) > 1 { fmt.Fprintf(out, "\n%s:\n", obj.Name) } if err := describe.PrintTemplateParameters(obj.Parameters, out); err != nil { fmt.Fprintf(cmd.Out(), "error printing parameters for %q: %v\n", obj.Name, err) } continue } if label := kcmdutil.GetFlagString(cmd, "labels"); len(label) > 0 { lbl, err := kubectl.ParseLabels(label) if err != nil { fmt.Fprintf(cmd.Out(), "error parsing labels: %v\n", err) continue } if obj.ObjectLabels == nil { obj.ObjectLabels = make(map[string]string) } for key, value := range lbl { obj.ObjectLabels[key] = value } } // Override the values for the current template parameters // when user specify the --value if cmd.Flag("value").Changed { values := kcmdutil.GetFlagStringSlice(cmd, "value") injectUserVars(values, out, obj) } injectUserVars(valueArgs, out, obj) resultObj, err := client.TemplateConfigs(namespace).Create(obj) if err != nil { fmt.Fprintf(cmd.Out(), "error processing the template %q: %v\n", obj.Name, err) continue } if outputFormat == "describe" { if s, err := (&describe.TemplateDescriber{ MetadataAccessor: meta.NewAccessor(), ObjectTyper: kapi.Scheme, ObjectDescriber: nil, }).DescribeTemplate(resultObj); err != nil { fmt.Fprintf(cmd.Out(), "error describing %q: %v\n", obj.Name, err) } else { fmt.Fprintf(out, s) } continue } objects = append(objects, resultObj.Objects...) } // Do not print the processed templates when asked to only show parameters or // describe. if kcmdutil.GetFlagBool(cmd, "parameters") || outputFormat == "describe" { return nil } p, _, err := kubectl.GetPrinter(outputFormat, "") if err != nil { return err } gv := mapping.GroupVersionKind.GroupVersion() version, err := kcmdutil.OutputVersion(cmd, &gv) if err != nil { return err } p = kubectl.NewVersionedPrinter(p, kapi.Scheme, version) // use generic output if kcmdutil.GetFlagBool(cmd, "raw") { for i := range objects { p.PrintObj(objects[i], out) } return nil } return p.PrintObj(&kapi.List{ ListMeta: unversioned.ListMeta{}, Items: objects, }, out) }
// RunProject contains all the necessary functionality for the OpenShift cli process command func RunProcess(f *clientcmd.Factory, out io.Writer, cmd *cobra.Command, args []string) error { templateName, valueArgs := "", []string{} for _, s := range args { isValue := strings.Contains(s, "=") switch { case isValue: valueArgs = append(valueArgs, s) case !isValue && len(templateName) == 0: templateName = s case !isValue && len(templateName) > 0: return kcmdutil.UsageError(cmd, "template name must be specified only once: %s", s) } } keys := sets.NewString() duplicatedKeys := sets.NewString() var flagValues []string if cmd.Flag("value").Changed { flagValues = kcmdutil.GetFlagStringSlice(cmd, "value") } for _, value := range flagValues { key := strings.Split(value, "=")[0] if keys.Has(key) { duplicatedKeys.Insert(key) } keys.Insert(key) } for _, value := range valueArgs { key := strings.Split(value, "=")[0] if keys.Has(key) { duplicatedKeys.Insert(key) } keys.Insert(key) } if len(duplicatedKeys) != 0 { return kcmdutil.UsageError(cmd, fmt.Sprintf("The following values were provided more than once: %s", strings.Join(duplicatedKeys.List(), ", "))) } filename := kcmdutil.GetFlagString(cmd, "filename") if len(templateName) == 0 && len(filename) == 0 { return kcmdutil.UsageError(cmd, "Must pass a filename or name of stored template") } if kcmdutil.GetFlagBool(cmd, "parameters") { for _, flag := range []string{"value", "labels", "output", "output-version", "raw", "template"} { if f := cmd.Flags().Lookup(flag); f != nil && f.Changed { return kcmdutil.UsageError(cmd, "The --parameters flag does not process the template, can't be used with --%v", flag) } } } namespace, explicit, err := f.DefaultNamespace() if err != nil { return err } mapper, typer := f.Object(false) client, _, err := f.Clients() if err != nil { return err } var ( objects []runtime.Object infos []*resource.Info ) mapping, err := mapper.RESTMapping(templateapi.Kind("Template")) if err != nil { return err } // When templateName is not empty, then we fetch the template from the // server, otherwise we require to set the `-f` parameter. if len(templateName) > 0 { var ( storedTemplate, rs string sourceNamespace string ok bool ) sourceNamespace, rs, storedTemplate, ok = parseNamespaceResourceName(templateName, namespace) if !ok { return fmt.Errorf("invalid argument %q", templateName) } if len(rs) > 0 && (rs != "template" && rs != "templates") { return fmt.Errorf("unable to process invalid resource %q", rs) } if len(storedTemplate) == 0 { return fmt.Errorf("invalid value syntax %q", templateName) } templateObj, err := client.Templates(sourceNamespace).Get(storedTemplate) if err != nil { if errors.IsNotFound(err) { return fmt.Errorf("template %q could not be found", storedTemplate) } return err } templateObj.CreationTimestamp = unversioned.Now() infos = append(infos, &resource.Info{Object: templateObj}) } else { infos, err = resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), kapi.Codecs.UniversalDecoder()). NamespaceParam(namespace).RequireNamespace(). FilenameParam(explicit, false, filename). Do(). Infos() if err != nil { return err } } outputFormat := kcmdutil.GetFlagString(cmd, "output") if len(infos) > 1 { // in order to run validation on the input given to us by a user, we only support the processing // of one template in a list. For instance, we want to be able to fail when a user does not give // a parameter that the template wants or when they give a parameter the template doesn't need, // as this may indicate that they have mis-used `oc process`. This is much less complicated when // we process at most one template. fmt.Fprintf(out, "%d input templates found, but only the first will be processed", len(infos)) } obj, ok := infos[0].Object.(*templateapi.Template) if !ok { sourceName := filename if len(templateName) > 0 { sourceName = namespace + "/" + templateName } return fmt.Errorf("unable to parse %q, not a valid Template but %s\n", sourceName, reflect.TypeOf(infos[0].Object)) } // If 'parameters' flag is set it does not do processing but only print // the template parameters to console for inspection. if kcmdutil.GetFlagBool(cmd, "parameters") { return describe.PrintTemplateParameters(obj.Parameters, out) } if label := kcmdutil.GetFlagString(cmd, "labels"); len(label) > 0 { lbl, err := kubectl.ParseLabels(label) if err != nil { return fmt.Errorf("error parsing labels: %v\n", err) } if obj.ObjectLabels == nil { obj.ObjectLabels = make(map[string]string) } for key, value := range lbl { obj.ObjectLabels[key] = value } } // Override the values for the current template parameters // when user specify the --value if cmd.Flag("value").Changed { values := kcmdutil.GetFlagStringSlice(cmd, "value") if errs := injectUserVars(values, obj); errs != nil { return kerrors.NewAggregate(errs) } } if errs := injectUserVars(valueArgs, obj); errs != nil { return kerrors.NewAggregate(errs) } resultObj, err := client.TemplateConfigs(namespace).Create(obj) if err != nil { return fmt.Errorf("error processing the template %q: %v\n", obj.Name, err) } if outputFormat == "describe" { if s, err := (&describe.TemplateDescriber{ MetadataAccessor: meta.NewAccessor(), ObjectTyper: kapi.Scheme, ObjectDescriber: nil, }).DescribeTemplate(resultObj); err != nil { return fmt.Errorf("error describing %q: %v\n", obj.Name, err) } else { _, err := fmt.Fprintf(out, s) return err } } objects = append(objects, resultObj.Objects...) // Do not print the processed templates when asked to only show parameters or // describe. if kcmdutil.GetFlagBool(cmd, "parameters") || outputFormat == "describe" { return nil } p, _, err := kubectl.GetPrinter(outputFormat, "") if err != nil { return err } gv := mapping.GroupVersionKind.GroupVersion() version, err := kcmdutil.OutputVersion(cmd, &gv) if err != nil { return err } p = kubectl.NewVersionedPrinter(p, kapi.Scheme, version) // use generic output if kcmdutil.GetFlagBool(cmd, "raw") { for i := range objects { p.PrintObj(objects[i], out) } return nil } return p.PrintObj(&kapi.List{ ListMeta: unversioned.ListMeta{}, Items: objects, }, out) }