github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/exec/task_config_source.go (about)

     1  package exec
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"strings"
     8  
     9  	"code.cloudfoundry.org/lager"
    10  	"github.com/concourse/baggageclaim"
    11  	"github.com/pf-qiu/concourse/v6/atc"
    12  	"github.com/pf-qiu/concourse/v6/atc/exec/build"
    13  	"github.com/pf-qiu/concourse/v6/atc/worker"
    14  	"github.com/pf-qiu/concourse/v6/vars"
    15  	"sigs.k8s.io/yaml"
    16  )
    17  
    18  //go:generate counterfeiter . TaskConfigSource
    19  
    20  // TaskConfigSource is used to determine a Task step's TaskConfig.
    21  type TaskConfigSource interface {
    22  	// FetchConfig returns the TaskConfig, and may have to a task config file out
    23  	// of the artifact.Repository.
    24  	FetchConfig(context.Context, lager.Logger, *build.Repository) (atc.TaskConfig, error)
    25  	Warnings() []string
    26  }
    27  
    28  // StaticConfigSource represents a statically configured TaskConfig.
    29  type StaticConfigSource struct {
    30  	Config *atc.TaskConfig
    31  }
    32  
    33  // FetchConfig returns the configuration.
    34  func (configSource StaticConfigSource) FetchConfig(context.Context, lager.Logger, *build.Repository) (atc.TaskConfig, error) {
    35  	taskConfig := atc.TaskConfig{}
    36  	if configSource.Config != nil {
    37  		taskConfig = *configSource.Config
    38  	}
    39  	return taskConfig, nil
    40  }
    41  
    42  func (configSource StaticConfigSource) Warnings() []string {
    43  	return []string{}
    44  }
    45  
    46  // FileConfigSource represents a dynamically configured TaskConfig, which will
    47  // be fetched from a specified file in the artifact.Repository.
    48  type FileConfigSource struct {
    49  	ConfigPath string
    50  	Client     worker.Client
    51  }
    52  
    53  // FetchConfig reads the specified file from the artifact.Repository and loads the
    54  // TaskConfig contained therein (expecting it to be YAML format).
    55  //
    56  // The path must be in the format SOURCE_NAME/FILE/PATH.yml. The SOURCE_NAME
    57  // will be used to determine the StreamableArtifactSource in the artifact.Repository to
    58  // stream the file out of.
    59  //
    60  // If the source name is missing (i.e. if the path is just "foo.yml"),
    61  // UnspecifiedArtifactSourceError is returned.
    62  //
    63  // If the specified source name cannot be found, UnknownArtifactSourceError is
    64  // returned.
    65  //
    66  // If the task config file is not found, or is invalid YAML, or is an invalid
    67  // task configuration, the respective errors will be bubbled up.
    68  func (configSource FileConfigSource) FetchConfig(ctx context.Context, logger lager.Logger, repo *build.Repository) (atc.TaskConfig, error) {
    69  	segs := strings.SplitN(configSource.ConfigPath, "/", 2)
    70  	if len(segs) != 2 {
    71  		return atc.TaskConfig{}, UnspecifiedArtifactSourceError{configSource.ConfigPath}
    72  	}
    73  
    74  	sourceName := build.ArtifactName(segs[0])
    75  	filePath := segs[1]
    76  
    77  	artifact, found := repo.ArtifactFor(sourceName)
    78  	if !found {
    79  		return atc.TaskConfig{}, UnknownArtifactSourceError{sourceName, configSource.ConfigPath}
    80  	}
    81  	stream, err := configSource.Client.StreamFileFromArtifact(ctx, logger, artifact, filePath)
    82  	if err != nil {
    83  		if err == baggageclaim.ErrFileNotFound {
    84  			return atc.TaskConfig{}, fmt.Errorf("task config '%s/%s' not found", sourceName, filePath)
    85  		}
    86  		return atc.TaskConfig{}, err
    87  	}
    88  
    89  	defer stream.Close()
    90  
    91  	byteConfig, err := ioutil.ReadAll(stream)
    92  	if err != nil {
    93  		return atc.TaskConfig{}, err
    94  	}
    95  
    96  	config, err := atc.NewTaskConfig(byteConfig)
    97  	if err != nil {
    98  		return atc.TaskConfig{}, fmt.Errorf("failed to create task config from bytes %s: %s", configSource.ConfigPath, err)
    99  	}
   100  
   101  	return config, nil
   102  }
   103  
   104  func (configSource FileConfigSource) Warnings() []string {
   105  	return []string{}
   106  }
   107  
   108  // BaseResourceTypeDefaultsApplySource applies base resource type defaults to image_source.
   109  type BaseResourceTypeDefaultsApplySource struct {
   110  	ConfigSource  TaskConfigSource
   111  	ResourceTypes atc.VersionedResourceTypes
   112  }
   113  
   114  func (configSource BaseResourceTypeDefaultsApplySource) FetchConfig(ctx context.Context, logger lager.Logger, repo *build.Repository) (atc.TaskConfig, error) {
   115  	config, err := configSource.ConfigSource.FetchConfig(ctx, logger, repo)
   116  	if err != nil {
   117  		return config, err
   118  	}
   119  
   120  	config.ImageResource.ApplySourceDefaults(configSource.ResourceTypes)
   121  
   122  	return config, nil
   123  }
   124  
   125  func (configSource BaseResourceTypeDefaultsApplySource) Warnings() []string {
   126  	return []string{}
   127  }
   128  
   129  // OverrideParamsConfigSource is used to override params in a config source
   130  type OverrideParamsConfigSource struct {
   131  	ConfigSource TaskConfigSource
   132  	Params       atc.TaskEnv
   133  	WarningList  []string
   134  }
   135  
   136  // FetchConfig overrides parameters, allowing the user to set params required by a task loaded
   137  // from a file by providing them in static configuration.
   138  func (configSource *OverrideParamsConfigSource) FetchConfig(ctx context.Context, logger lager.Logger, source *build.Repository) (atc.TaskConfig, error) {
   139  	taskConfig, err := configSource.ConfigSource.FetchConfig(ctx, logger, source)
   140  	if err != nil {
   141  		return atc.TaskConfig{}, err
   142  	}
   143  
   144  	if taskConfig.Params == nil {
   145  		taskConfig.Params = atc.TaskEnv{}
   146  	}
   147  
   148  	for key, val := range configSource.Params {
   149  		if _, exists := taskConfig.Params[key]; !exists {
   150  			configSource.WarningList = append(configSource.WarningList, fmt.Sprintf("%s was defined in pipeline but missing from task file", key))
   151  		}
   152  
   153  		taskConfig.Params[key] = val
   154  	}
   155  
   156  	return taskConfig, nil
   157  }
   158  
   159  func (configSource OverrideParamsConfigSource) Warnings() []string {
   160  	return configSource.WarningList
   161  }
   162  
   163  // InterpolateTemplateConfigSource represents a config source interpolated by template vars
   164  type InterpolateTemplateConfigSource struct {
   165  	ConfigSource  TaskConfigSource
   166  	Vars          []vars.Variables
   167  	ExpectAllKeys bool
   168  }
   169  
   170  // FetchConfig returns the interpolated configuration
   171  func (configSource InterpolateTemplateConfigSource) FetchConfig(ctx context.Context, logger lager.Logger, source *build.Repository) (atc.TaskConfig, error) {
   172  	taskConfig, err := configSource.ConfigSource.FetchConfig(ctx, logger, source)
   173  	if err != nil {
   174  		return atc.TaskConfig{}, err
   175  	}
   176  
   177  	byteConfig, err := yaml.Marshal(taskConfig)
   178  	if err != nil {
   179  		return atc.TaskConfig{}, fmt.Errorf("failed to marshal task config: %s", err)
   180  	}
   181  
   182  	// process task config using the provided variables
   183  	byteConfig, err = vars.NewTemplateResolver(byteConfig, configSource.Vars).Resolve(configSource.ExpectAllKeys, true)
   184  	if err != nil {
   185  		return atc.TaskConfig{}, fmt.Errorf("failed to interpolate task config: %s", err)
   186  	}
   187  
   188  	taskConfig, err = atc.NewTaskConfig(byteConfig)
   189  	if err != nil {
   190  		return atc.TaskConfig{}, fmt.Errorf("failed to create task config from bytes: %s", err)
   191  	}
   192  
   193  	return taskConfig, nil
   194  }
   195  
   196  func (configSource InterpolateTemplateConfigSource) Warnings() []string {
   197  	return []string{}
   198  }
   199  
   200  // ValidatingConfigSource delegates to another ConfigSource, and validates its
   201  // task config.
   202  type ValidatingConfigSource struct {
   203  	ConfigSource TaskConfigSource
   204  }
   205  
   206  // FetchConfig fetches the config using the underlying ConfigSource, and checks
   207  // that it's valid.
   208  func (configSource ValidatingConfigSource) FetchConfig(ctx context.Context, logger lager.Logger, source *build.Repository) (atc.TaskConfig, error) {
   209  	config, err := configSource.ConfigSource.FetchConfig(ctx, logger, source)
   210  	if err != nil {
   211  		return atc.TaskConfig{}, err
   212  	}
   213  
   214  	if err := config.Validate(); err != nil {
   215  		return atc.TaskConfig{}, err
   216  	}
   217  
   218  	return config, nil
   219  }
   220  
   221  func (configSource ValidatingConfigSource) Warnings() []string {
   222  	return configSource.ConfigSource.Warnings()
   223  }
   224  
   225  // UnknownArtifactSourceError is returned when the artifact.ArtifactName specified by the
   226  // path does not exist in the artifact.Repository.
   227  type UnknownArtifactSourceError struct {
   228  	SourceName build.ArtifactName
   229  	ConfigPath string
   230  }
   231  
   232  // Error returns a human-friendly error message.
   233  func (err UnknownArtifactSourceError) Error() string {
   234  	return fmt.Sprintf("unknown artifact source: '%s' in task config file path '%s'", err.SourceName, err.ConfigPath)
   235  }
   236  
   237  // UnspecifiedArtifactSourceError is returned when the specified path is of a
   238  // file in the toplevel directory, and so it does not indicate a SourceName.
   239  type UnspecifiedArtifactSourceError struct {
   240  	Path string
   241  }
   242  
   243  // Error returns a human-friendly error message.
   244  func (err UnspecifiedArtifactSourceError) Error() string {
   245  	return fmt.Sprintf("config path '%s' does not specify where the file lives", err.Path)
   246  }