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 }