github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/tag/custom_template.go (about)

     1  /*
     2  Copyright 2020 The Skaffold Authors
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package tag
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"fmt"
    23  	"text/template"
    24  
    25  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log"
    26  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
    27  )
    28  
    29  // customTemplateTagger implements Tagger
    30  type customTemplateTagger struct {
    31  	Template   *template.Template
    32  	Components map[string]Tagger
    33  }
    34  
    35  // NewCustomTemplateTagger creates a new customTemplateTagger
    36  func NewCustomTemplateTagger(t string, components map[string]Tagger) (Tagger, error) {
    37  	tmpl, err := ParseCustomTemplate(t)
    38  	if err != nil {
    39  		return nil, fmt.Errorf("parsing template: %w", err)
    40  	}
    41  
    42  	return &customTemplateTagger{
    43  		Template:   tmpl,
    44  		Components: components,
    45  	}, nil
    46  }
    47  
    48  // GenerateTag generates a tag from a template referencing tagging strategies.
    49  func (t *customTemplateTagger) GenerateTag(ctx context.Context, image latest.Artifact) (string, error) {
    50  	customMap, err := t.EvaluateComponents(ctx, image)
    51  	if err != nil {
    52  		return "", err
    53  	}
    54  
    55  	// missingkey=error throws error when map is indexed with an undefined key
    56  	tag, err := ExecuteCustomTemplate(t.Template.Option("missingkey=error"), customMap)
    57  	if err != nil {
    58  		return "", err
    59  	}
    60  
    61  	return tag, nil
    62  }
    63  
    64  // EvaluateComponents creates a custom mapping of component names to their tagger string representation.
    65  func (t *customTemplateTagger) EvaluateComponents(ctx context.Context, image latest.Artifact) (map[string]string, error) {
    66  	customMap := map[string]string{}
    67  
    68  	gitTagger, _ := NewGitCommit("", "", false)
    69  	dateTimeTagger := NewDateTimeTagger("", "")
    70  
    71  	for k, v := range map[string]Tagger{"GIT": gitTagger, "DATE": dateTimeTagger, "SHA": &ChecksumTagger{}} {
    72  		tag, _ := v.GenerateTag(ctx, image)
    73  		customMap[k] = tag
    74  	}
    75  
    76  	for k, v := range t.Components {
    77  		if _, ok := v.(*customTemplateTagger); ok {
    78  			return nil, fmt.Errorf("invalid component specified in custom template: %v", v)
    79  		}
    80  		tag, err := v.GenerateTag(ctx, image)
    81  		if err != nil {
    82  			return nil, fmt.Errorf("evaluating custom template component: %w", err)
    83  		}
    84  		customMap[k] = tag
    85  	}
    86  	return customMap, nil
    87  }
    88  
    89  // ParseCustomTemplate is a simple wrapper to parse an custom template.
    90  func ParseCustomTemplate(t string) (*template.Template, error) {
    91  	return template.New("customTemplate").Parse(t)
    92  }
    93  
    94  // ExecuteCustomTemplate executes a customTemplate against a custom map.
    95  func ExecuteCustomTemplate(customTemplate *template.Template, customMap map[string]string) (string, error) {
    96  	var buf bytes.Buffer
    97  	log.Entry(context.TODO()).Debugf("Executing custom template %v with custom map %v", customTemplate, customMap)
    98  	if err := customTemplate.Execute(&buf, customMap); err != nil {
    99  		return "", fmt.Errorf("executing template: %w", err)
   100  	}
   101  	return buf.String(), nil
   102  }