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

     1  package builds_test
     2  
     3  import (
     4  	"encoding/json"
     5  	"testing"
     6  
     7  	"github.com/pf-qiu/concourse/v6/atc"
     8  	"github.com/pf-qiu/concourse/v6/atc/builds"
     9  	"github.com/pf-qiu/concourse/v6/atc/db"
    10  	"github.com/stretchr/testify/require"
    11  	"github.com/stretchr/testify/suite"
    12  )
    13  
    14  type PlannerSuite struct {
    15  	suite.Suite
    16  	*require.Assertions
    17  }
    18  
    19  func TestPlanner(t *testing.T) {
    20  	suite.Run(t, &PlannerSuite{
    21  		Assertions: require.New(t),
    22  	})
    23  }
    24  
    25  type PlannerTest struct {
    26  	Title string
    27  
    28  	Config atc.StepConfig
    29  	Inputs []db.BuildInput
    30  
    31  	CompareIDs bool
    32  	PlanJSON   string
    33  	Err        error
    34  }
    35  
    36  var resources = db.SchedulerResources{
    37  	db.SchedulerResource{
    38  		Name:   "some-resource",
    39  		Type:   "some-resource-type",
    40  		Source: atc.Source{"some": "source"},
    41  	},
    42  	db.SchedulerResource{
    43  		Name:   "some-base-resource",
    44  		Type:   "some-base-resource-type",
    45  		Source: atc.Source{"some": "source"},
    46  	},
    47  }
    48  
    49  var resourceTypes = atc.VersionedResourceTypes{
    50  	{
    51  		ResourceType: atc.ResourceType{
    52  			Name:     "some-resource-type",
    53  			Type:     "some-base-resource-type",
    54  			Source:   atc.Source{"some": "type-source"},
    55  			Defaults: atc.Source{"default-key": "default-value"},
    56  		},
    57  		Version: atc.Version{"some": "type-version"},
    58  	},
    59  }
    60  
    61  var baseResourceTypeDefaults = map[string]atc.Source{
    62  	"some-base-resource-type": {"default-key": "default-value"},
    63  }
    64  
    65  var factoryTests = []PlannerTest{
    66  	{
    67  		Title: "get step",
    68  		Config: &atc.GetStep{
    69  			Name:     "some-name",
    70  			Resource: "some-resource",
    71  			Params:   atc.Params{"some": "params"},
    72  			Version:  &atc.VersionConfig{Pinned: atc.Version{"doesnt": "matter"}},
    73  			Tags:     atc.Tags{"tag-1", "tag-2"},
    74  		},
    75  		Inputs: []db.BuildInput{
    76  			{
    77  				Name:    "some-name",
    78  				Version: atc.Version{"some": "version"},
    79  			},
    80  		},
    81  		PlanJSON: `{
    82  			"id": "(unique)",
    83  			"get": {
    84  				"name": "some-name",
    85  				"type": "some-resource-type",
    86  				"resource": "some-resource",
    87  				"source": {"some":"source","default-key":"default-value"},
    88  				"params": {"some":"params"},
    89  				"version": {"some":"version"},
    90  				"tags": ["tag-1", "tag-2"],
    91  				"resource_types": [
    92  					{
    93  						"name": "some-resource-type",
    94  						"type": "some-base-resource-type",
    95  						"source": {"some": "type-source"},
    96  						"defaults": {"default-key":"default-value"},
    97  						"version": {"some": "type-version"}
    98  					}
    99  				]
   100  			}
   101  		}`,
   102  	},
   103  	{
   104  		Title: "get step with base resource type",
   105  		Config: &atc.GetStep{
   106  			Name:     "some-name",
   107  			Resource: "some-base-resource",
   108  			Params:   atc.Params{"some": "params"},
   109  			Version:  &atc.VersionConfig{Pinned: atc.Version{"doesnt": "matter"}},
   110  			Tags:     atc.Tags{"tag-1", "tag-2"},
   111  		},
   112  		Inputs: []db.BuildInput{
   113  			{
   114  				Name:    "some-name",
   115  				Version: atc.Version{"some": "version"},
   116  			},
   117  		},
   118  		PlanJSON: `{
   119  			"id": "(unique)",
   120  			"get": {
   121  				"name": "some-name",
   122  				"type": "some-base-resource-type",
   123  				"resource": "some-base-resource",
   124  				"source": {"some":"source","default-key":"default-value"},
   125  				"params": {"some":"params"},
   126  				"version": {"some":"version"},
   127  				"tags": ["tag-1", "tag-2"],
   128  				"resource_types": [
   129  					{
   130  						"name": "some-resource-type",
   131  						"type": "some-base-resource-type",
   132  						"source": {"some": "type-source"},
   133  						"defaults": {"default-key":"default-value"},
   134  						"version": {"some": "type-version"}
   135  					}
   136  				]
   137  			}
   138  		}`,
   139  	},
   140  	{
   141  		Title: "get step with unknown resource",
   142  		Config: &atc.GetStep{
   143  			Name:     "some-name",
   144  			Resource: "bogus-resource",
   145  		},
   146  		Err: builds.UnknownResourceError{Resource: "bogus-resource"},
   147  	},
   148  	{
   149  		Title: "get step with no available version",
   150  		Config: &atc.GetStep{
   151  			Name:     "some-name",
   152  			Resource: "some-resource",
   153  		},
   154  		Err: builds.VersionNotProvidedError{Input: "some-name"},
   155  	},
   156  	{
   157  		Title: "put step",
   158  		Config: &atc.PutStep{
   159  			Name:      "some-name",
   160  			Resource:  "some-resource",
   161  			Params:    atc.Params{"some": "params"},
   162  			Tags:      atc.Tags{"tag-1", "tag-2"},
   163  			Inputs:    &atc.InputsConfig{All: true},
   164  			GetParams: atc.Params{"some": "get-params"},
   165  		},
   166  		Inputs: []db.BuildInput{
   167  			{
   168  				Name:    "some-name",
   169  				Version: atc.Version{"some": "version"},
   170  			},
   171  		},
   172  
   173  		// the ids are significant for versioned_from
   174  		CompareIDs: true,
   175  		PlanJSON: `{
   176  			"id": "3",
   177  			"on_success": {
   178  				"step": {
   179  					"id": "1",
   180  					"put": {
   181  						"name": "some-name",
   182  						"type": "some-resource-type",
   183  						"resource": "some-resource",
   184  						"inputs": "all",
   185  						"source": {"some":"source","default-key":"default-value"},
   186  						"params": {"some":"params"},
   187  						"tags": ["tag-1", "tag-2"],
   188  						"resource_types": [
   189  							{
   190  								"name": "some-resource-type",
   191  								"type": "some-base-resource-type",
   192  								"source": {"some": "type-source"},
   193  								"defaults": {"default-key":"default-value"},
   194  								"version": {"some": "type-version"}
   195  							}
   196  						]
   197  					}
   198  				},
   199  				"on_success": {
   200  					"id": "2",
   201  					"get": {
   202  						"name": "some-name",
   203  						"type": "some-resource-type",
   204  						"resource": "some-resource",
   205  						"source": {"some":"source","default-key":"default-value"},
   206  						"params": {"some":"get-params"},
   207  						"tags": ["tag-1", "tag-2"],
   208  						"version_from": "1",
   209  						"resource_types": [
   210  							{
   211  								"name": "some-resource-type",
   212  								"type": "some-base-resource-type",
   213  								"source": {"some": "type-source"},
   214  								"defaults": {"default-key":"default-value"},
   215  								"version": {"some": "type-version"}
   216  							}
   217  						]
   218  					}
   219  				}
   220  			}
   221  		}`,
   222  	},
   223  	{
   224  		Title: "task step",
   225  
   226  		Config: &atc.TaskStep{
   227  			Name:       "some-task",
   228  			Privileged: true,
   229  			Config: &atc.TaskConfig{
   230  				Platform: "linux",
   231  				Run:      atc.TaskRunConfig{Path: "hello"},
   232  			},
   233  			ConfigPath:        "some-task-file",
   234  			Vars:              atc.Params{"some": "vars"},
   235  			Params:            atc.TaskEnv{"SOME": "PARAMS"},
   236  			Tags:              atc.Tags{"tag-1", "tag-2"},
   237  			InputMapping:      map[string]string{"generic": "specific"},
   238  			OutputMapping:     map[string]string{"specific": "generic"},
   239  			ImageArtifactName: "some-image",
   240  		},
   241  
   242  		PlanJSON: `{
   243  			"id": "(unique)",
   244  			"task": {
   245  				"name": "some-task",
   246  				"privileged": true,
   247  				"config": {
   248  					"platform": "linux",
   249  					"run": {"path": "hello"}
   250  				},
   251  				"config_path": "some-task-file",
   252  				"vars": {"some": "vars"},
   253  				"params": {"SOME": "PARAMS"},
   254  				"tags": ["tag-1", "tag-2"],
   255  				"input_mapping": {"generic": "specific"},
   256  				"output_mapping": {"specific": "generic"},
   257  				"image": "some-image",
   258  				"resource_types": [
   259  					{
   260  						"name": "some-resource-type",
   261  						"type": "some-base-resource-type",
   262  						"source": {"some": "type-source"},
   263  						"defaults": {"default-key":"default-value"},
   264  						"version": {"some": "type-version"}
   265  					}
   266  				]
   267  			}
   268  		}`,
   269  	},
   270  	{
   271  		Title: "set_pipeline step",
   272  
   273  		Config: &atc.SetPipelineStep{
   274  			Name:         "some-pipeline",
   275  			File:         "some-pipeline-file",
   276  			Vars:         atc.Params{"some": "vars"},
   277  			VarFiles:     []string{"file-1", "file-2"},
   278  			InstanceVars: atc.InstanceVars{"branch": "feature/foo"},
   279  		},
   280  
   281  		PlanJSON: `{
   282  			"id": "(unique)",
   283  			"set_pipeline": {
   284  				"name": "some-pipeline",
   285  				"file": "some-pipeline-file",
   286  				"vars": {"some": "vars"},
   287  				"var_files": ["file-1", "file-2"],
   288  				"instance_vars": {"branch": "feature/foo"}
   289  			}
   290  		}`,
   291  	},
   292  	{
   293  		Title: "load_var step",
   294  
   295  		Config: &atc.LoadVarStep{
   296  			Name:   "some-var",
   297  			File:   "some-var-file",
   298  			Format: "raw",
   299  			Reveal: true,
   300  		},
   301  
   302  		PlanJSON: `{
   303  			"id": "(unique)",
   304  			"load_var": {
   305  				"name": "some-var",
   306  				"file": "some-var-file",
   307  				"format": "raw",
   308  				"reveal": true
   309  			}
   310  		}`,
   311  	},
   312  	{
   313  		Title: "try step",
   314  
   315  		Config: &atc.TryStep{
   316  			Step: atc.Step{
   317  				Config: &atc.LoadVarStep{
   318  					Name: "some-var",
   319  					File: "some-file",
   320  				},
   321  			},
   322  		},
   323  
   324  		PlanJSON: `{
   325  			"id": "(unique)",
   326  			"try": {
   327  				"step": {
   328  					"id": "(unique)",
   329  					"load_var": {
   330  						"name": "some-var",
   331  						"file": "some-file"
   332  					}
   333  				}
   334  			}
   335  		}`,
   336  	},
   337  	{
   338  		Title: "do step",
   339  
   340  		Config: &atc.DoStep{
   341  			Steps: []atc.Step{
   342  				{
   343  					Config: &atc.LoadVarStep{
   344  						Name: "some-var",
   345  						File: "some-file",
   346  					},
   347  				},
   348  				{
   349  					Config: &atc.LoadVarStep{
   350  						Name: "some-other-var",
   351  						File: "some-other-file",
   352  					},
   353  				},
   354  			},
   355  		},
   356  
   357  		PlanJSON: `{
   358  			"id": "(unique)",
   359  			"do": [
   360  				{
   361  					"id": "(unique)",
   362  					"load_var": {
   363  						"name": "some-var",
   364  						"file": "some-file"
   365  					}
   366  				},
   367  				{
   368  					"id": "(unique)",
   369  					"load_var": {
   370  						"name": "some-other-var",
   371  						"file": "some-other-file"
   372  					}
   373  				}
   374  			]
   375  		}`,
   376  	},
   377  	{
   378  		Title: "in_parallel step",
   379  
   380  		Config: &atc.InParallelStep{
   381  			Config: atc.InParallelConfig{
   382  				Limit:    3,
   383  				FailFast: true,
   384  				Steps: []atc.Step{
   385  					{
   386  						Config: &atc.LoadVarStep{
   387  							Name: "some-var",
   388  							File: "some-file",
   389  						},
   390  					},
   391  					{
   392  						Config: &atc.LoadVarStep{
   393  							Name: "some-other-var",
   394  							File: "some-other-file",
   395  						},
   396  					},
   397  				},
   398  			},
   399  		},
   400  
   401  		PlanJSON: `{
   402  			"id": "(unique)",
   403  			"in_parallel": {
   404  				"steps": [
   405  					{
   406  						"id": "(unique)",
   407  						"load_var": {
   408  							"name": "some-var",
   409  							"file": "some-file"
   410  						}
   411  					},
   412  					{
   413  						"id": "(unique)",
   414  						"load_var": {
   415  							"name": "some-other-var",
   416  							"file": "some-other-file"
   417  						}
   418  					}
   419  				],
   420  				"limit": 3,
   421  				"fail_fast": true
   422  			}
   423  		}`,
   424  	},
   425  	{
   426  		Title: "aggregate step",
   427  
   428  		Config: &atc.AggregateStep{
   429  			Steps: []atc.Step{
   430  				{
   431  					Config: &atc.LoadVarStep{
   432  						Name: "some-var",
   433  						File: "some-file",
   434  					},
   435  				},
   436  				{
   437  					Config: &atc.LoadVarStep{
   438  						Name: "some-other-var",
   439  						File: "some-other-file",
   440  					},
   441  				},
   442  			},
   443  		},
   444  
   445  		PlanJSON: `{
   446  			"id": "(unique)",
   447  			"aggregate": [
   448  				{
   449  					"id": "(unique)",
   450  					"load_var": {
   451  						"name": "some-var",
   452  						"file": "some-file"
   453  					}
   454  				},
   455  				{
   456  					"id": "(unique)",
   457  					"load_var": {
   458  						"name": "some-other-var",
   459  						"file": "some-other-file"
   460  					}
   461  				}
   462  			]
   463  		}`,
   464  	},
   465  	{
   466  		Title: "across step",
   467  
   468  		Config: &atc.AcrossStep{
   469  			Step: &atc.LoadVarStep{
   470  				Name: "some-var",
   471  				File: "some-file",
   472  			},
   473  			Vars: []atc.AcrossVarConfig{
   474  				{
   475  					Var:         "var1",
   476  					Values:      []interface{}{"a1", "a2"},
   477  					MaxInFlight: &atc.MaxInFlightConfig{All: true},
   478  				},
   479  				{
   480  					Var:         "var2",
   481  					Values:      []interface{}{"b1", "b2"},
   482  					MaxInFlight: &atc.MaxInFlightConfig{Limit: 1},
   483  				},
   484  			},
   485  		},
   486  
   487  		PlanJSON: `{
   488  			"id": "(unique)",
   489  			"across": {
   490  				"vars": [
   491  					{
   492  						"name": "var1",
   493  						"values": ["a1", "a2"],
   494  						"max_in_flight": "all"
   495  					},
   496  					{
   497  						"name": "var2",
   498  						"values": ["b1", "b2"],
   499  						"max_in_flight": 1
   500  					}
   501  				],
   502  				"steps": [
   503  					{
   504  						"values": ["a1", "b1"],
   505  						"step": {
   506  							"id": "(unique)",
   507  							"load_var": {
   508  								"name": "some-var",
   509  								"file": "some-file"
   510  							}
   511  						}
   512  					},
   513  					{
   514  						"values": ["a1", "b2"],
   515  						"step": {
   516  							"id": "(unique)",
   517  							"load_var": {
   518  								"name": "some-var",
   519  								"file": "some-file"
   520  							}
   521  						}
   522  					},
   523  					{
   524  						"values": ["a2", "b1"],
   525  						"step": {
   526  							"id": "(unique)",
   527  							"load_var": {
   528  								"name": "some-var",
   529  								"file": "some-file"
   530  							}
   531  						}
   532  					},
   533  					{
   534  						"values": ["a2", "b2"],
   535  						"step": {
   536  							"id": "(unique)",
   537  							"load_var": {
   538  								"name": "some-var",
   539  								"file": "some-file"
   540  							}
   541  						}
   542  					}
   543  				]
   544  			}
   545  		}`,
   546  	},
   547  	{
   548  		Title: "timeout modifier",
   549  
   550  		Config: &atc.TimeoutStep{
   551  			Step: &atc.LoadVarStep{
   552  				Name: "some-var",
   553  				File: "some-file",
   554  			},
   555  			Duration: "1h",
   556  		},
   557  
   558  		PlanJSON: `{
   559  			"id": "(unique)",
   560  			"timeout": {
   561  				"step": {
   562  					"id": "(unique)",
   563  					"load_var": {
   564  						"name": "some-var",
   565  						"file": "some-file"
   566  					}
   567  				},
   568  				"duration": "1h"
   569  			}
   570  		}`,
   571  	},
   572  	{
   573  		Title: "attempts modifier",
   574  
   575  		Config: &atc.RetryStep{
   576  			Step: &atc.LoadVarStep{
   577  				Name: "some-var",
   578  				File: "some-file",
   579  			},
   580  			Attempts: 3,
   581  		},
   582  
   583  		CompareIDs: true,
   584  		PlanJSON: `{
   585  			"id": "4",
   586  			"retry": [
   587  				{
   588  					"id": "1",
   589  					"load_var": {
   590  						"name": "some-var",
   591  						"file": "some-file"
   592  					}
   593  				},
   594  				{
   595  					"id": "2",
   596  					"load_var": {
   597  						"name": "some-var",
   598  						"file": "some-file"
   599  					}
   600  				},
   601  				{
   602  					"id": "3",
   603  					"load_var": {
   604  						"name": "some-var",
   605  						"file": "some-file"
   606  					}
   607  				}
   608  			]
   609  		}`,
   610  	},
   611  	{
   612  		Title: "on_success step",
   613  
   614  		Config: &atc.OnSuccessStep{
   615  			Step: &atc.LoadVarStep{
   616  				Name: "some-var",
   617  				File: "some-file",
   618  			},
   619  			Hook: atc.Step{
   620  				Config: &atc.LoadVarStep{
   621  					Name: "some-other-var",
   622  					File: "some-other-file",
   623  				},
   624  			},
   625  		},
   626  
   627  		PlanJSON: `{
   628  			"id": "(unique)",
   629  			"on_success": {
   630  				"step": {
   631  					"id": "(unique)",
   632  					"load_var": {
   633  						"name": "some-var",
   634  						"file": "some-file"
   635  					}
   636  				},
   637  				"on_success": {
   638  					"id": "(unique)",
   639  					"load_var": {
   640  						"name": "some-other-var",
   641  						"file": "some-other-file"
   642  					}
   643  				}
   644  			}
   645  		}`,
   646  	},
   647  	{
   648  		Title: "on_failure step",
   649  
   650  		Config: &atc.OnFailureStep{
   651  			Step: &atc.LoadVarStep{
   652  				Name: "some-var",
   653  				File: "some-file",
   654  			},
   655  			Hook: atc.Step{
   656  				Config: &atc.LoadVarStep{
   657  					Name: "some-other-var",
   658  					File: "some-other-file",
   659  				},
   660  			},
   661  		},
   662  
   663  		PlanJSON: `{
   664  			"id": "(unique)",
   665  			"on_failure": {
   666  				"step": {
   667  					"id": "(unique)",
   668  					"load_var": {
   669  						"name": "some-var",
   670  						"file": "some-file"
   671  					}
   672  				},
   673  				"on_failure": {
   674  					"id": "(unique)",
   675  					"load_var": {
   676  						"name": "some-other-var",
   677  						"file": "some-other-file"
   678  					}
   679  				}
   680  			}
   681  		}`,
   682  	},
   683  	{
   684  		Title: "on_error step",
   685  
   686  		Config: &atc.OnErrorStep{
   687  			Step: &atc.LoadVarStep{
   688  				Name: "some-var",
   689  				File: "some-file",
   690  			},
   691  			Hook: atc.Step{
   692  				Config: &atc.LoadVarStep{
   693  					Name: "some-other-var",
   694  					File: "some-other-file",
   695  				},
   696  			},
   697  		},
   698  
   699  		PlanJSON: `{
   700  			"id": "(unique)",
   701  			"on_error": {
   702  				"step": {
   703  					"id": "(unique)",
   704  					"load_var": {
   705  						"name": "some-var",
   706  						"file": "some-file"
   707  					}
   708  				},
   709  				"on_error": {
   710  					"id": "(unique)",
   711  					"load_var": {
   712  						"name": "some-other-var",
   713  						"file": "some-other-file"
   714  					}
   715  				}
   716  			}
   717  		}`,
   718  	},
   719  	{
   720  		Title: "on_abort step",
   721  
   722  		Config: &atc.OnAbortStep{
   723  			Step: &atc.LoadVarStep{
   724  				Name: "some-var",
   725  				File: "some-file",
   726  			},
   727  			Hook: atc.Step{
   728  				Config: &atc.LoadVarStep{
   729  					Name: "some-other-var",
   730  					File: "some-other-file",
   731  				},
   732  			},
   733  		},
   734  
   735  		PlanJSON: `{
   736  			"id": "(unique)",
   737  			"on_abort": {
   738  				"step": {
   739  					"id": "(unique)",
   740  					"load_var": {
   741  						"name": "some-var",
   742  						"file": "some-file"
   743  					}
   744  				},
   745  				"on_abort": {
   746  					"id": "(unique)",
   747  					"load_var": {
   748  						"name": "some-other-var",
   749  						"file": "some-other-file"
   750  					}
   751  				}
   752  			}
   753  		}`,
   754  	},
   755  	{
   756  		Title: "ensure step",
   757  
   758  		Config: &atc.EnsureStep{
   759  			Step: &atc.LoadVarStep{
   760  				Name: "some-var",
   761  				File: "some-file",
   762  			},
   763  			Hook: atc.Step{
   764  				Config: &atc.LoadVarStep{
   765  					Name: "some-other-var",
   766  					File: "some-other-file",
   767  				},
   768  			},
   769  		},
   770  
   771  		PlanJSON: `{
   772  			"id": "(unique)",
   773  			"ensure": {
   774  				"step": {
   775  					"id": "(unique)",
   776  					"load_var": {
   777  						"name": "some-var",
   778  						"file": "some-file"
   779  					}
   780  				},
   781  				"ensure": {
   782  					"id": "(unique)",
   783  					"load_var": {
   784  						"name": "some-other-var",
   785  						"file": "some-other-file"
   786  					}
   787  				}
   788  			}
   789  		}`,
   790  	},
   791  }
   792  
   793  func (test PlannerTest) Run(s *PlannerSuite) {
   794  	factory := builds.NewPlanner(atc.NewPlanFactory(0))
   795  
   796  	actualPlan, actualErr := factory.Create(test.Config, resources, resourceTypes, test.Inputs)
   797  
   798  	if test.Err != nil {
   799  		s.Equal(test.Err, actualErr)
   800  		return
   801  	} else {
   802  		s.NoError(actualErr)
   803  	}
   804  
   805  	seenIDs := map[atc.PlanID]bool{}
   806  	actualPlan.Each(func(p *atc.Plan) {
   807  		s.NotEmpty(p.ID)
   808  
   809  		// make sure all IDs are unique
   810  		s.False(seenIDs[p.ID], "duplicate plan id: %s", p.ID)
   811  		seenIDs[p.ID] = true
   812  
   813  		// strip out the IDs, we don't really care what their value is
   814  		if !test.CompareIDs {
   815  			p.ID = "(unique)"
   816  		}
   817  	})
   818  	s.NotEmpty(seenIDs)
   819  
   820  	actualJSON, err := json.Marshal(actualPlan)
   821  	s.NoError(err)
   822  
   823  	s.JSONEq(test.PlanJSON, string(actualJSON))
   824  }
   825  
   826  func (s *PlannerSuite) TestFactory() {
   827  	atc.LoadBaseResourceTypeDefaults(baseResourceTypeDefaults)
   828  	for _, test := range factoryTests {
   829  		s.Run(test.Title, func() {
   830  			test.Run(s)
   831  		})
   832  	}
   833  	atc.LoadBaseResourceTypeDefaults(map[string]atc.Source{})
   834  }