package template import ( "fmt" "regexp" "strings" kubeapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/golang/glog" config "github.com/openshift/origin/pkg/config/api" "github.com/openshift/origin/pkg/template/api" . "github.com/openshift/origin/pkg/template/generator" ) var parameterExp = regexp.MustCompile(`\$\{([a-zA-Z0-9\_]+)\}`) // TemplateProcessor transforms Template objects into Config objects. type TemplateProcessor struct { Generators map[string]Generator } // NewTemplateProcessor creates new TemplateProcessor and initializes // its set of generators. func NewTemplateProcessor(generators map[string]Generator) *TemplateProcessor { return &TemplateProcessor{Generators: generators} } // Process transforms Template object into Config object. It generates // Parameter values using the defined set of generators first, and then it // substitutes all Parameter expression occurances with their corresponding // values (currently in the containers' Environment variables only). func (p *TemplateProcessor) Process(template *api.Template) (*config.Config, error) { if err := p.GenerateParameterValues(template); err != nil { return nil, err } if err := p.SubstituteParameters(template); err != nil { return nil, err } config := &config.Config{ Name: template.Name, Description: template.Description, Items: template.Items, } config.ID = template.ID config.Kind = "Config" config.CreationTimestamp = util.Now() return config, nil } // AddParameter adds new custom parameter to the Template. It overrides // the existing parameter, if already defined. func (p *TemplateProcessor) AddParameter(t *api.Template, param api.Parameter) { if existing := p.GetParameterByName(t, param.Name); existing != nil { *existing = param } else { t.Parameters = append(t.Parameters, param) } } // GetParameterByName searches for a Parameter in the Template // based on it's name. func (p *TemplateProcessor) GetParameterByName(t *api.Template, name string) *api.Parameter { for i, param := range t.Parameters { if param.Name == name { return &(t.Parameters[i]) } } return nil } // SubstituteParameters loops over all Environment variables defined for // all ReplicationController and Pod containers and substitutes all // Parameter expression occurances with their corresponding values. // // Example of Parameter expression: // - ${PARAMETER_NAME} // // TODO: Implement substitution for more types and fields. func (p *TemplateProcessor) SubstituteParameters(t *api.Template) error { // Make searching for given parameter name/value more effective paramMap := make(map[string]string, len(t.Parameters)) for _, param := range t.Parameters { paramMap[param.Name] = param.Value } for i, item := range t.Items { switch obj := item.Object.(type) { case *kubeapi.ReplicationController: p.substituteParametersInManifest(&obj.DesiredState.PodTemplate.DesiredState.Manifest, paramMap) t.Items[i] = runtime.EmbeddedObject{Object: obj} case *kubeapi.Pod: p.substituteParametersInManifest(&obj.DesiredState.Manifest, paramMap) t.Items[i] = runtime.EmbeddedObject{Object: obj} default: glog.V(1).Infof("Parameter substitution not implemented for resource '%T'.", obj) } } return nil } // substituteParametersInManifest is a helper function that iterates // over the given manifest and substitutes all Parameter expression // occurances with their corresponding values. func (p *TemplateProcessor) substituteParametersInManifest(manifest *kubeapi.ContainerManifest, paramMap map[string]string) { for i, _ := range manifest.Containers { for e, _ := range manifest.Containers[i].Env { envValue := &manifest.Containers[i].Env[e].Value // Match all parameter expressions found in the given env var for _, match := range parameterExp.FindAllStringSubmatch(*envValue, -1) { // Substitute expression with its value, if corresponding parameter found if len(match) > 1 { if paramValue, found := paramMap[match[1]]; found { *envValue = strings.Replace(*envValue, match[0], paramValue, 1) } } } } } } // GenerateParameterValues generates Value for each Parameter of the given // Template that has Expression field specified and doesn't have any // Value yet. // // Examples (Expression => Value): // - "test[0-9]{1}x" => "test7x" // - "[0-1]{8}" => "01001100" // - "0x[A-F0-9]{4}" => "0xB3AF" // - "[a-zA-Z0-9]{8}" => "hW4yQU5i" func (p *TemplateProcessor) GenerateParameterValues(t *api.Template) error { for i, _ := range t.Parameters { param := &t.Parameters[i] if param.Expression != "" && param.Value == "" { generator, ok := p.Generators["expression"] if !ok { return fmt.Errorf("Can't find expression generator.") } value, err := generator.GenerateValue(param.Expression) if err != nil { return err } param.Value, ok = value.(string) if !ok { return fmt.Errorf("Can't convert the generated value %v to string.", value) } } } return nil }