Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding a field to templates to allow them to deliver a user message w… #9708

Merged
merged 1 commit into from
Jul 11, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions api/swagger-spec/oapi-v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -24900,23 +24900,27 @@
"$ref": "v1.ObjectMeta",
"description": "Standard object's metadata."
},
"message": {
"type": "string",
"description": "message is an optional instructional message that will be displayed when this template is instantiated. This field should inform the user how to utilize the newly created resources. Parameter substitution will be performed on the message before being displayed so that generated credentials and other parameters can be included in the output."
},
"objects": {
"type": "array",
"items": {
"$ref": "runtime.RawExtension"
},
"description": "Objects is an array of objects to include in this template. Required."
"description": "objects is an array of resources to include in this template."
},
"parameters": {
"type": "array",
"items": {
"$ref": "v1.Parameter"
},
"description": "Optional: Parameters is an array of Parameters used during the Template to Config transformation."
"description": "parameters is an optional array of Parameters used during the Template to Config transformation."
},
"labels": {
"type": "object",
"description": "Labels is a set of labels that are applied to every object during the Template to Config transformation. Optional"
"description": "labels is a optional set of labels that are applied to every object during the Template to Config transformation."
}
}
},
Expand Down
11 changes: 11 additions & 0 deletions pkg/cmd/cli/describe/describer.go
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,15 @@ type TemplateDescriber struct {
kctl.ObjectDescriber
}

// DescribeMessage prints the message that will be parameter substituted and displayed to the
// user when this template is processed.
func (d *TemplateDescriber) DescribeMessage(msg string, out *tabwriter.Writer) {
if len(msg) == 0 {
msg = "<none>"
}
formatString(out, "Message", msg)
}

// DescribeParameters prints out information about the parameters of a template
func (d *TemplateDescriber) DescribeParameters(params []templateapi.Parameter, out *tabwriter.Writer) {
formatString(out, "Parameters", " ")
Expand Down Expand Up @@ -954,6 +963,8 @@ func (d *TemplateDescriber) DescribeTemplate(template *templateapi.Template) (st
out.Write([]byte("\n"))
formatString(out, "Object Labels", formatLabels(template.ObjectLabels))
out.Write([]byte("\n"))
d.DescribeMessage(template.Message, out)
out.Write([]byte("\n"))
out.Flush()
d.describeObjects(template.Objects, out)
return nil
Expand Down
1 change: 1 addition & 0 deletions pkg/template/api/deep_copy_generated.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func DeepCopy_api_Template(in Template, out *Template, c *conversion.Cloner) err
if err := api.DeepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
out.Message = in.Message
if in.Parameters != nil {
in, out := in.Parameters, &out.Parameters
*out = make([]Parameter, len(in))
Expand Down
16 changes: 12 additions & 4 deletions pkg/template/api/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,23 @@ type Template struct {
unversioned.TypeMeta
kapi.ObjectMeta

// Optional: Parameters is an array of Parameters used during the
// message is an optional instructional message that will
// be displayed when this template is instantiated.
// This field should inform the user how to utilize the newly created resources.
// Parameter substitution will be performed on the message before being
// displayed so that generated credentials and other parameters can be
// included in the output.
Message string

// parameters is an optional array of Parameters used during the
// Template to Config transformation.
Parameters []Parameter

// Required: A list of resources to create
// objects is an array of resources to include in this template.
Objects []runtime.Object

// Optional: ObjectLabels is a set of labels that are applied to every
// object during the Template to Config transformation
// objectLabels is an optional set of labels that are applied to every
// object during the Template to Config transformation.
ObjectLabels map[string]string
}

Expand Down
1 change: 1 addition & 0 deletions pkg/template/api/v1/deep_copy_generated.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ func DeepCopy_v1_Template(in Template, out *Template, c *conversion.Cloner) erro
if err := api_v1.DeepCopy_v1_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
out.Message = in.Message
if in.Objects != nil {
in, out := in.Objects, &out.Objects
*out = make([]runtime.RawExtension, len(in))
Expand Down
7 changes: 4 additions & 3 deletions pkg/template/api/v1/swagger_doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ func (Parameter) SwaggerDoc() map[string]string {
var map_Template = map[string]string{
"": "Template contains the inputs needed to produce a Config.",
"metadata": "Standard object's metadata.",
"objects": "Objects is an array of objects to include in this template. Required.",
"parameters": "Optional: Parameters is an array of Parameters used during the Template to Config transformation.",
"labels": "Labels is a set of labels that are applied to every object during the Template to Config transformation. Optional",
"message": "message is an optional instructional message that will be displayed when this template is instantiated. This field should inform the user how to utilize the newly created resources. Parameter substitution will be performed on the message before being displayed so that generated credentials and other parameters can be included in the output.",
"objects": "objects is an array of resources to include in this template.",
"parameters": "parameters is an optional array of Parameters used during the Template to Config transformation.",
"labels": "labels is a optional set of labels that are applied to every object during the Template to Config transformation.",
}

func (Template) SwaggerDoc() map[string]string {
Expand Down
16 changes: 12 additions & 4 deletions pkg/template/api/v1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,23 @@ type Template struct {
// Standard object's metadata.
kapi.ObjectMeta `json:"metadata,omitempty"`

// Objects is an array of objects to include in this template. Required.
// message is an optional instructional message that will
// be displayed when this template is instantiated.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by instantiated? You can't output this message in the oc process template | oc create -f flow, right? We're adding a template API field for the the new-app only flow? @jwforres would need to special case this in the webconsole too.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@deads2k yes this is being added to the webconsole, @jwforres is aware of it. in fact it's the primary use case for adding this.

would you be happier with "optional message that may be displayed depending which client you use and whether it bothers to support this field"?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would you be happier with "optional message that may be displayed depending which client you use and whether it bothers to support this field"?

Yeah, along with, "in our default client, we ignore this in the default processing/creation workflow".

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

our default client is new-app and it's not going to ignore it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

our default client is new-app and it's not going to ignore it.

As opposed the oc process command we built specifically to instantiate templates?

Copy link
Contributor

@bparees bparees Jul 7, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

correct. oc process got deprecated when @smarterclayton decided to add template processing to new-app.

oc process is for advanced users who want to pipe to oc apply or something else.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Instantiated - my initial wording was "processed", but it was suggested that the only reason we process templates is to instantiate the underlying objects in etcd.
  • Correct, the present implementation does not attempt to output the message during "oc process" (or create). Only targeting new-app and the web console with @jwforres 's help.

// This field should inform the user how to utilize the newly created resources.
// Parameter substitution will be performed on the message before being
// displayed so that generated credentials and other parameters can be
// included in the output.
Message string `json:"message,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to start this godoc w/ "message"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bparees It appears to in my view of the commit. There are several inline comments that split the full godoc in half in the github diff view.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whoops, you're right, it was just such a natural start to the comment I didn't think to scroll up, my bad.


// objects is an array of resources to include in this template.
Objects []runtime.RawExtension `json:"objects"`

// Optional: Parameters is an array of Parameters used during the
// parameters is an optional array of Parameters used during the
// Template to Config transformation.
Parameters []Parameter `json:"parameters,omitempty"`

// Labels is a set of labels that are applied to every
// object during the Template to Config transformation. Optional
// labels is a optional set of labels that are applied to every
// object during the Template to Config transformation.
Labels map[string]string `json:"labels,omitempty"`
}

Expand Down
43 changes: 26 additions & 17 deletions pkg/template/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@ func (p *Processor) Process(template *api.Template) field.ErrorList {
return append(templateErrors, fieldError)
}

// Place parameters into a map for efficient lookup
paramMap := make(map[string]api.Parameter)
for _, param := range template.Parameters {
paramMap[param.Name] = param
}

// Perform parameter substitution on the template's user message. This can be used to
// instruct a user on next steps for the template.
template.Message = p.EvaluateParameterSubstitution(paramMap, template.Message)

itemPath := field.NewPath("item")
for i, item := range template.Objects {
idxPath := itemPath.Index(i)
Expand All @@ -51,7 +61,7 @@ func (p *Processor) Process(template *api.Template) field.ErrorList {
item = decodedObj
}

newItem, err := p.SubstituteParameters(template.Parameters, item)
newItem, err := p.SubstituteParameters(paramMap, item)
if err != nil {
templateErrors = append(templateErrors, field.Invalid(idxPath.Child("parameters"), template.Parameters, err.Error()))
}
Expand Down Expand Up @@ -114,30 +124,29 @@ func GetParameterByName(t *api.Template, name string) *api.Parameter {
return nil
}

// EvaluateParameterSubstitution replaces escaped parameters in a string with values from the
// provided map.
func (p *Processor) EvaluateParameterSubstitution(params map[string]api.Parameter, in string) string {
for _, match := range parameterExp.FindAllStringSubmatch(in, -1) {
if len(match) > 1 {
if paramValue, found := params[match[1]]; found {
in = strings.Replace(in, match[0], paramValue.Value, 1)
}
}
}
return in
}

// SubstituteParameters loops over all values defined in structured
// and unstructured types that are children of item.
//
// Example of Parameter expression:
// - ${PARAMETER_NAME}
//
func (p *Processor) SubstituteParameters(params []api.Parameter, item runtime.Object) (runtime.Object, error) {
// Make searching for given parameter name/value more effective
paramMap := make(map[string]string, len(params))
for _, param := range params {
paramMap[param.Name] = param.Value
}

func (p *Processor) SubstituteParameters(params map[string]api.Parameter, item runtime.Object) (runtime.Object, error) {
stringreplace.VisitObjectStrings(item, func(in string) string {
for _, match := range parameterExp.FindAllStringSubmatch(in, -1) {
if len(match) > 1 {
if paramValue, found := paramMap[match[1]]; found {
in = strings.Replace(in, match[0], paramValue, 1)
}
}
}
return in
return p.EvaluateParameterSubstitution(params, in)
})

return item, nil
}

Expand Down
1 change: 1 addition & 0 deletions test/templates/testdata/guestbook.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"description": "Example shows how to build a simple multi-tier application using Kubernetes and Docker"
}
},
"message": "Your admin credentials are ${ADMIN_USERNAME}:${ADMIN_PASSWORD}",
"objects": [
{
"kind": "Route",
Expand Down
3 changes: 2 additions & 1 deletion test/templates/testdata/guestbook_list.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"description": "Example shows how to build a simple multi-tier application using Kubernetes and Docker"
}
},
"message": "Your admin credentials are adminQ3H:dwNJiJwW",
"objects": [
{
"apiVersion": "v1",
Expand Down Expand Up @@ -310,4 +311,4 @@
"value": "1"
}
]
}
}