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

     1  package exec_test
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"code.cloudfoundry.org/lager/lagertest"
     9  	"github.com/concourse/baggageclaim"
    10  	"github.com/pf-qiu/concourse/v6/atc"
    11  	. "github.com/pf-qiu/concourse/v6/atc/exec"
    12  	"github.com/pf-qiu/concourse/v6/atc/exec/build"
    13  	"github.com/pf-qiu/concourse/v6/atc/exec/execfakes"
    14  	"github.com/pf-qiu/concourse/v6/atc/runtime/runtimefakes"
    15  	"github.com/pf-qiu/concourse/v6/atc/worker/workerfakes"
    16  	"github.com/pf-qiu/concourse/v6/vars"
    17  	. "github.com/onsi/ginkgo"
    18  	. "github.com/onsi/gomega"
    19  	"github.com/onsi/gomega/gbytes"
    20  	"sigs.k8s.io/yaml"
    21  )
    22  
    23  var _ = Describe("TaskConfigSource", func() {
    24  	var (
    25  		taskConfig atc.TaskConfig
    26  		taskVars   atc.Params
    27  		repo       *build.Repository
    28  		logger     *lagertest.TestLogger
    29  	)
    30  
    31  	BeforeEach(func() {
    32  		logger = lagertest.NewTestLogger("task-config-source-test")
    33  		repo = build.NewRepository()
    34  		taskConfig = atc.TaskConfig{
    35  			Platform:  "some-platform",
    36  			RootfsURI: "some-image",
    37  			ImageResource: &atc.ImageResource{
    38  				Type: "docker",
    39  				Source: atc.Source{
    40  					"a":               "b",
    41  					"evaluated-value": "((task-variable-name))",
    42  				},
    43  				Params: atc.Params{
    44  					"some":            "params",
    45  					"evaluated-value": "((task-variable-name))",
    46  				},
    47  				Version: atc.Version{"some": "version"},
    48  			},
    49  			Params: atc.TaskEnv{
    50  				"key1": "key1-((task-variable-name))",
    51  				"key2": "key2-((task-variable-name))",
    52  			},
    53  			Run: atc.TaskRunConfig{
    54  				Path: "ls",
    55  				Args: []string{"-al", "((task-variable-name))"},
    56  				Dir:  "some/dir",
    57  				User: "some-user",
    58  			},
    59  			Inputs: []atc.TaskInputConfig{
    60  				{Name: "some-input", Path: "some-path"},
    61  			},
    62  		}
    63  		taskVars = atc.Params{
    64  			"task-variable-name": "task-variable-value",
    65  		}
    66  	})
    67  
    68  	Describe("StaticConfigSource", func() {
    69  
    70  		It("fetches task config successfully", func() {
    71  			configSource := StaticConfigSource{Config: &taskConfig}
    72  			fetchedConfig, fetchErr := configSource.FetchConfig(context.TODO(), logger, repo)
    73  			Expect(fetchErr).ToNot(HaveOccurred())
    74  			Expect(fetchedConfig).To(Equal(taskConfig))
    75  		})
    76  
    77  		It("fetches config of nil task successfully", func() {
    78  			configSource := StaticConfigSource{Config: nil}
    79  			fetchedConfig, fetchErr := configSource.FetchConfig(context.TODO(), logger, repo)
    80  			Expect(fetchErr).ToNot(HaveOccurred())
    81  			Expect(fetchedConfig).To(Equal(atc.TaskConfig{}))
    82  		})
    83  	})
    84  
    85  	Describe("FileConfigSource", func() {
    86  		var (
    87  			configSource     FileConfigSource
    88  			fakeWorkerClient *workerfakes.FakeClient
    89  			fetchErr         error
    90  			artifactName     string
    91  		)
    92  
    93  		BeforeEach(func() {
    94  
    95  			artifactName = "some-artifact-name"
    96  			fakeWorkerClient = new(workerfakes.FakeClient)
    97  			configSource = FileConfigSource{
    98  				ConfigPath: artifactName + "/build.yml",
    99  				Client:     fakeWorkerClient,
   100  			}
   101  		})
   102  
   103  		JustBeforeEach(func() {
   104  			_, fetchErr = configSource.FetchConfig(context.TODO(), logger, repo)
   105  		})
   106  
   107  		Context("when the path does not indicate an artifact source", func() {
   108  			BeforeEach(func() {
   109  				configSource.ConfigPath = "foo-bar.yml"
   110  			})
   111  
   112  			It("returns an error", func() {
   113  				Expect(fetchErr).To(Equal(UnspecifiedArtifactSourceError{"foo-bar.yml"}))
   114  			})
   115  		})
   116  
   117  		Context("when the file's artifact can be found in the repository", func() {
   118  			var fakeArtifact *runtimefakes.FakeArtifact
   119  
   120  			BeforeEach(func() {
   121  				fakeArtifact = new(runtimefakes.FakeArtifact)
   122  				repo.RegisterArtifact(build.ArtifactName(artifactName), fakeArtifact)
   123  			})
   124  
   125  			Context("when the artifact provides a proper file", func() {
   126  				var streamedOut *gbytes.Buffer
   127  
   128  				BeforeEach(func() {
   129  					marshalled, err := yaml.Marshal(taskConfig)
   130  					Expect(err).NotTo(HaveOccurred())
   131  
   132  					streamedOut = gbytes.BufferWithBytes(marshalled)
   133  					fakeWorkerClient.StreamFileFromArtifactReturns(streamedOut, nil)
   134  				})
   135  
   136  				It("fetches the file via the correct artifact & path", func() {
   137  					_, _, artifact, dest := fakeWorkerClient.StreamFileFromArtifactArgsForCall(0)
   138  					Expect(artifact).To(Equal(fakeArtifact))
   139  					Expect(dest).To(Equal("build.yml"))
   140  				})
   141  
   142  				It("succeeds", func() {
   143  					Expect(fetchErr).NotTo(HaveOccurred())
   144  				})
   145  
   146  				It("closes the stream", func() {
   147  					Expect(streamedOut.Closed()).To(BeTrue())
   148  				})
   149  			})
   150  
   151  			Context("when the artifact source provides an invalid configuration", func() {
   152  				var streamedOut *gbytes.Buffer
   153  
   154  				BeforeEach(func() {
   155  					invalidConfig := taskConfig
   156  					invalidConfig.Platform = ""
   157  					invalidConfig.Run = atc.TaskRunConfig{}
   158  
   159  					marshalled, err := yaml.Marshal(invalidConfig)
   160  					Expect(err).NotTo(HaveOccurred())
   161  
   162  					streamedOut = gbytes.BufferWithBytes(marshalled)
   163  					fakeWorkerClient.StreamFileFromArtifactReturns(streamedOut, nil)
   164  				})
   165  
   166  				It("returns an error", func() {
   167  					Expect(fetchErr).To(HaveOccurred())
   168  				})
   169  			})
   170  
   171  			Context("when the artifact source provides a malformed file", func() {
   172  				var streamedOut *gbytes.Buffer
   173  
   174  				BeforeEach(func() {
   175  					streamedOut = gbytes.BufferWithBytes([]byte("bogus"))
   176  					fakeWorkerClient.StreamFileFromArtifactReturns(streamedOut, nil)
   177  				})
   178  
   179  				It("fails", func() {
   180  					Expect(fetchErr).To(HaveOccurred())
   181  				})
   182  
   183  				It("closes the stream", func() {
   184  					Expect(streamedOut.Closed()).To(BeTrue())
   185  				})
   186  			})
   187  
   188  			Context("when the artifact source provides a valid file with invalid keys", func() {
   189  				var streamedOut *gbytes.Buffer
   190  
   191  				BeforeEach(func() {
   192  					streamedOut = gbytes.BufferWithBytes([]byte(`
   193  platform: beos
   194  
   195  intputs: []
   196  
   197  run: {path: a/file}
   198  `))
   199  					fakeWorkerClient.StreamFileFromArtifactReturns(streamedOut, nil)
   200  				})
   201  
   202  				It("fails", func() {
   203  					Expect(fetchErr).To(HaveOccurred())
   204  				})
   205  
   206  				It("closes the stream", func() {
   207  					Expect(streamedOut.Closed()).To(BeTrue())
   208  				})
   209  			})
   210  
   211  			Context("when streaming the file out fails", func() {
   212  				disaster := errors.New("nope")
   213  
   214  				BeforeEach(func() {
   215  					fakeWorkerClient.StreamFileFromArtifactReturns(nil, disaster)
   216  				})
   217  
   218  				It("returns the error", func() {
   219  					Expect(fetchErr).To(HaveOccurred())
   220  				})
   221  			})
   222  
   223  			Context("when the file task is not found", func() {
   224  				BeforeEach(func() {
   225  					fakeWorkerClient.StreamFileFromArtifactReturns(nil, baggageclaim.ErrFileNotFound)
   226  				})
   227  
   228  				It("returns the error", func() {
   229  					Expect(fetchErr).To(HaveOccurred())
   230  					Expect(fetchErr.Error()).To(Equal(fmt.Sprintf("task config '%s/build.yml' not found", artifactName)))
   231  				})
   232  			})
   233  		})
   234  
   235  		Context("when the file's artifact source cannot be found in the repository", func() {
   236  			It("returns an UnknownArtifactSourceError", func() {
   237  				Expect(fetchErr).To(Equal(UnknownArtifactSourceError{SourceName: build.ArtifactName(artifactName), ConfigPath: artifactName + "/build.yml"}))
   238  			})
   239  		})
   240  	})
   241  
   242  	Describe("OverrideParamsConfigSource", func() {
   243  		var (
   244  			config       atc.TaskConfig
   245  			configSource TaskConfigSource
   246  
   247  			overrideParams atc.TaskEnv
   248  
   249  			fetchedConfig atc.TaskConfig
   250  			fetchErr      error
   251  		)
   252  
   253  		BeforeEach(func() {
   254  			config = atc.TaskConfig{
   255  				Platform:  "some-platform",
   256  				RootfsURI: "some-image",
   257  				Params:    atc.TaskEnv{"PARAM": "A", "ORIG_PARAM": "D"},
   258  				Run: atc.TaskRunConfig{
   259  					Path: "echo",
   260  					Args: []string{"bananapants"},
   261  				},
   262  			}
   263  
   264  			overrideParams = atc.TaskEnv{"PARAM": "B", "EXTRA_PARAM": "C"}
   265  		})
   266  
   267  		Context("when there are no params to override", func() {
   268  			BeforeEach(func() {
   269  				configSource = &OverrideParamsConfigSource{
   270  					ConfigSource: StaticConfigSource{Config: &config},
   271  				}
   272  			})
   273  
   274  			JustBeforeEach(func() {
   275  				fetchedConfig, fetchErr = configSource.FetchConfig(context.TODO(), logger, repo)
   276  			})
   277  
   278  			It("succeeds", func() {
   279  				Expect(fetchErr).NotTo(HaveOccurred())
   280  			})
   281  
   282  			It("returns the same config", func() {
   283  				Expect(fetchedConfig).To(Equal(config))
   284  			})
   285  
   286  			It("returns no warnings", func() {
   287  				Expect(configSource.Warnings()).To(HaveLen(0))
   288  			})
   289  		})
   290  
   291  		Context("when override params are specified", func() {
   292  			BeforeEach(func() {
   293  				configSource = &OverrideParamsConfigSource{
   294  					ConfigSource: StaticConfigSource{Config: &config},
   295  					Params:       overrideParams,
   296  				}
   297  			})
   298  
   299  			JustBeforeEach(func() {
   300  				fetchedConfig, fetchErr = configSource.FetchConfig(context.TODO(), logger, repo)
   301  			})
   302  
   303  			It("succeeds", func() {
   304  				Expect(fetchErr).NotTo(HaveOccurred())
   305  			})
   306  
   307  			It("returns the config with overridden parameters", func() {
   308  				Expect(fetchedConfig.Params).To(Equal(atc.TaskEnv{
   309  					"ORIG_PARAM":  "D",
   310  					"PARAM":       "B",
   311  					"EXTRA_PARAM": "C",
   312  				}))
   313  			})
   314  
   315  			It("returns a deprecation warning", func() {
   316  				Expect(configSource.Warnings()).To(HaveLen(1))
   317  				Expect(configSource.Warnings()[0]).To(ContainSubstring("EXTRA_PARAM was defined in pipeline but missing from task file"))
   318  			})
   319  		})
   320  	})
   321  
   322  	Describe("ValidatingConfigSource", func() {
   323  		var (
   324  			fakeConfigSource *execfakes.FakeTaskConfigSource
   325  
   326  			configSource TaskConfigSource
   327  
   328  			fetchedConfig atc.TaskConfig
   329  			fetchErr      error
   330  		)
   331  
   332  		BeforeEach(func() {
   333  			fakeConfigSource = new(execfakes.FakeTaskConfigSource)
   334  
   335  			configSource = ValidatingConfigSource{fakeConfigSource}
   336  		})
   337  
   338  		JustBeforeEach(func() {
   339  			fetchedConfig, fetchErr = configSource.FetchConfig(context.TODO(), logger, repo)
   340  		})
   341  
   342  		Context("when the config is valid", func() {
   343  			config := atc.TaskConfig{
   344  				Platform:  "some-platform",
   345  				RootfsURI: "some-image",
   346  				Params:    atc.TaskEnv{"PARAM": "A"},
   347  				Run: atc.TaskRunConfig{
   348  					Path: "echo",
   349  					Args: []string{"bananapants"},
   350  				},
   351  			}
   352  
   353  			BeforeEach(func() {
   354  				fakeConfigSource.FetchConfigReturns(config, nil)
   355  			})
   356  
   357  			It("returns the config and no error", func() {
   358  				Expect(fetchErr).ToNot(HaveOccurred())
   359  				Expect(fetchedConfig).To(Equal(config))
   360  			})
   361  		})
   362  
   363  		Context("when the config is invalid", func() {
   364  			BeforeEach(func() {
   365  				fakeConfigSource.FetchConfigReturns(atc.TaskConfig{
   366  					RootfsURI: "some-image",
   367  					Params:    atc.TaskEnv{"PARAM": "A"},
   368  					Run: atc.TaskRunConfig{
   369  						Args: []string{"bananapants"},
   370  					},
   371  				}, nil)
   372  			})
   373  
   374  			It("returns the validation error", func() {
   375  				Expect(fetchErr).To(HaveOccurred())
   376  			})
   377  		})
   378  
   379  		Context("when fetching the config fails", func() {
   380  			disaster := errors.New("nope")
   381  
   382  			BeforeEach(func() {
   383  				fakeConfigSource.FetchConfigReturns(atc.TaskConfig{}, disaster)
   384  			})
   385  
   386  			It("returns the error", func() {
   387  				Expect(fetchErr).To(Equal(disaster))
   388  			})
   389  		})
   390  	})
   391  
   392  	Describe("InterpolateTemplateConfigSource", func() {
   393  		var (
   394  			configSource  TaskConfigSource
   395  			fetchedConfig atc.TaskConfig
   396  			fetchErr      error
   397  			expectAllKeys bool
   398  		)
   399  
   400  		JustBeforeEach(func() {
   401  			configSource = StaticConfigSource{Config: &taskConfig}
   402  			configSource = InterpolateTemplateConfigSource{
   403  				ConfigSource:  configSource,
   404  				Vars:          []vars.Variables{vars.StaticVariables(taskVars)},
   405  				ExpectAllKeys: expectAllKeys,
   406  			}
   407  			fetchedConfig, fetchErr = configSource.FetchConfig(context.TODO(), logger, repo)
   408  		})
   409  
   410  		Context("when expect all keys", func() {
   411  			BeforeEach(func() {
   412  				expectAllKeys = true
   413  			})
   414  
   415  			It("fetches task config successfully", func() {
   416  				Expect(fetchErr).ToNot(HaveOccurred())
   417  			})
   418  
   419  			It("resolves task config parameters successfully", func() {
   420  				Expect(fetchedConfig.Run.Args).To(Equal([]string{"-al", "task-variable-value"}))
   421  				Expect(fetchedConfig.Params).To(Equal(atc.TaskEnv{
   422  					"key1": "key1-task-variable-value",
   423  					"key2": "key2-task-variable-value",
   424  				}))
   425  				Expect(fetchedConfig.ImageResource.Source).To(Equal(atc.Source{
   426  					"a":               "b",
   427  					"evaluated-value": "task-variable-value",
   428  				}))
   429  			})
   430  		})
   431  
   432  		Context("when not expect all keys", func() {
   433  			BeforeEach(func() {
   434  				expectAllKeys = false
   435  				taskVars = atc.Params{}
   436  			})
   437  
   438  			It("fetches task config successfully", func() {
   439  				Expect(fetchErr).ToNot(HaveOccurred())
   440  			})
   441  
   442  			It("resolves task config parameters successfully", func() {
   443  				Expect(fetchedConfig.Run.Args).To(Equal([]string{"-al", "((task-variable-name))"}))
   444  				Expect(fetchedConfig.Params).To(Equal(atc.TaskEnv{
   445  					"key1": "key1-((task-variable-name))",
   446  					"key2": "key2-((task-variable-name))",
   447  				}))
   448  				Expect(fetchedConfig.ImageResource.Source).To(Equal(atc.Source{
   449  					"a":               "b",
   450  					"evaluated-value": "((task-variable-name))",
   451  				}))
   452  			})
   453  		})
   454  	})
   455  
   456  	Context("BaseResourceTypeDefaultsApplySource", func() {
   457  		var (
   458  			configSource  TaskConfigSource
   459  			resourceTypes atc.VersionedResourceTypes
   460  			fetchedConfig atc.TaskConfig
   461  			fetchErr      error
   462  		)
   463  
   464  		JustBeforeEach(func() {
   465  			configSource = StaticConfigSource{Config: &taskConfig}
   466  			configSource = BaseResourceTypeDefaultsApplySource{
   467  				ConfigSource:  configSource,
   468  				ResourceTypes: resourceTypes,
   469  			}
   470  			fetchedConfig, fetchErr = configSource.FetchConfig(context.TODO(), logger, repo)
   471  		})
   472  
   473  		Context("resourceTypes is empty, and no base resource type defaults configured", func() {
   474  			It("fetchedConfig should be identical to the original", func() {
   475  				Expect(fetchErr).ToNot(HaveOccurred())
   476  				Expect(fetchedConfig).To(Equal(taskConfig))
   477  			})
   478  		})
   479  
   480  		Context("resourceTypes is empty, and base resource type defaults configured", func() {
   481  			BeforeEach(func() {
   482  				atc.LoadBaseResourceTypeDefaults(map[string]atc.Source{"docker": atc.Source{"some-key": "some-value"}})
   483  			})
   484  			AfterEach(func() {
   485  				atc.LoadBaseResourceTypeDefaults(map[string]atc.Source{})
   486  			})
   487  
   488  			It("defaults should be added to image source", func() {
   489  				Expect(fetchErr).ToNot(HaveOccurred())
   490  				Expect(fetchedConfig.ImageResource.Source).To(Equal(atc.Source{
   491  					"a":               "b",
   492  					"evaluated-value": "((task-variable-name))",
   493  					"some-key":        "some-value",
   494  				}))
   495  			})
   496  		})
   497  
   498  		Context("resourceTypes contains image source type", func() {
   499  			BeforeEach(func() {
   500  				resourceTypes = atc.VersionedResourceTypes{
   501  					{
   502  						ResourceType: atc.ResourceType{
   503  							Name:     "docker",
   504  							Defaults: atc.Source{"some-key": "some-value"},
   505  						},
   506  					},
   507  				}
   508  			})
   509  
   510  			It("defaults should be added to image source", func() {
   511  				Expect(fetchErr).ToNot(HaveOccurred())
   512  				Expect(fetchedConfig.ImageResource.Source).To(Equal(atc.Source{
   513  					"a":               "b",
   514  					"evaluated-value": "((task-variable-name))",
   515  					"some-key":        "some-value",
   516  				}))
   517  			})
   518  		})
   519  	})
   520  })