github.com/kaisenlinux/docker@v0.0.0-20230510090727-ea55db55fac7/swarmkit/template/getter_test.go (about)

     1  package template
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/docker/swarmkit/agent"
     7  	"github.com/docker/swarmkit/api"
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  )
    11  
    12  func TestTemplatedSecret(t *testing.T) {
    13  	templatedSecret := &api.Secret{
    14  		ID: "templatedsecret",
    15  	}
    16  
    17  	referencedSecret := &api.Secret{
    18  		ID: "referencedsecret",
    19  		Spec: api.SecretSpec{
    20  			Data: []byte("mysecret"),
    21  		},
    22  	}
    23  	referencedConfig := &api.Config{
    24  		ID: "referencedconfig",
    25  		Spec: api.ConfigSpec{
    26  			Data: []byte("myconfig"),
    27  		},
    28  	}
    29  
    30  	type testCase struct {
    31  		desc        string
    32  		secretSpec  api.SecretSpec
    33  		task        *api.Task
    34  		node        *api.NodeDescription
    35  		expected    string
    36  		expectedErr string
    37  	}
    38  
    39  	testCases := []testCase{
    40  		{
    41  			desc: "Test expansion of task context",
    42  			secretSpec: api.SecretSpec{
    43  				Data: []byte("SERVICE_ID={{.Service.ID}}\n" +
    44  					"SERVICE_NAME={{.Service.Name}}\n" +
    45  					"TASK_ID={{.Task.ID}}\n" +
    46  					"TASK_NAME={{.Task.Name}}\n" +
    47  					"NODE_ID={{.Node.ID}}\n" +
    48  					"NODE_HOSTNAME={{.Node.Hostname}}\n" +
    49  					"NODE_OS={{.Node.Platform.OS}}\n" +
    50  					"NODE_ARCHITECTURE={{.Node.Platform.Architecture}}"),
    51  				Templating: &api.Driver{Name: "golang"},
    52  			},
    53  			expected: "SERVICE_ID=serviceID\n" +
    54  				"SERVICE_NAME=serviceName\n" +
    55  				"TASK_ID=taskID\n" +
    56  				"TASK_NAME=serviceName.10.taskID\n" +
    57  				"NODE_ID=nodeID\n" +
    58  				"NODE_HOSTNAME=myhostname\n" +
    59  				"NODE_OS=testOS\n" +
    60  				"NODE_ARCHITECTURE=testArchitecture",
    61  			task: modifyTask(func(t *api.Task) {
    62  				t.Spec = api.TaskSpec{
    63  					Runtime: &api.TaskSpec_Container{
    64  						Container: &api.ContainerSpec{
    65  							Secrets: []*api.SecretReference{
    66  								{
    67  									SecretID:   "templatedsecret",
    68  									SecretName: "templatedsecretname",
    69  								},
    70  							},
    71  						},
    72  					},
    73  				}
    74  			}),
    75  			node: modifyNode(func(n *api.NodeDescription) {
    76  				n.Hostname = "myhostname"
    77  				n.Platform.OS = "testOS"
    78  				n.Platform.Architecture = "testArchitecture"
    79  			}),
    80  		},
    81  		{
    82  			desc: "Test expansion of secret, by target",
    83  			secretSpec: api.SecretSpec{
    84  				Data:       []byte("SECRET_VAL={{secret \"referencedsecrettarget\"}}\n"),
    85  				Templating: &api.Driver{Name: "golang"},
    86  			},
    87  			expected: "SECRET_VAL=mysecret\n",
    88  			task: modifyTask(func(t *api.Task) {
    89  				t.Spec = api.TaskSpec{
    90  					Runtime: &api.TaskSpec_Container{
    91  						Container: &api.ContainerSpec{
    92  							Secrets: []*api.SecretReference{
    93  								{
    94  									SecretID:   "templatedsecret",
    95  									SecretName: "templatedsecretname",
    96  								},
    97  								{
    98  									SecretID:   "referencedsecret",
    99  									SecretName: "referencedsecretname",
   100  									Target: &api.SecretReference_File{
   101  										File: &api.FileTarget{
   102  											Name: "referencedsecrettarget",
   103  											UID:  "0",
   104  											GID:  "0",
   105  											Mode: 0666,
   106  										},
   107  									},
   108  								},
   109  							},
   110  						},
   111  					},
   112  				}
   113  			}),
   114  			node: modifyNode(func(n *api.NodeDescription) {
   115  				// use default values
   116  			}),
   117  		},
   118  		{
   119  			desc: "Test expansion of config, by target",
   120  			secretSpec: api.SecretSpec{
   121  				Data:       []byte("CONFIG_VAL={{config \"referencedconfigtarget\"}}\n"),
   122  				Templating: &api.Driver{Name: "golang"},
   123  			},
   124  			expected: "CONFIG_VAL=myconfig\n",
   125  			task: modifyTask(func(t *api.Task) {
   126  				t.Spec = api.TaskSpec{
   127  					Runtime: &api.TaskSpec_Container{
   128  						Container: &api.ContainerSpec{
   129  							Secrets: []*api.SecretReference{
   130  								{
   131  									SecretID:   "templatedsecret",
   132  									SecretName: "templatedsecretname",
   133  								},
   134  							},
   135  							Configs: []*api.ConfigReference{
   136  								{
   137  									ConfigID:   "referencedconfig",
   138  									ConfigName: "referencedconfigname",
   139  									Target: &api.ConfigReference_File{
   140  										File: &api.FileTarget{
   141  											Name: "referencedconfigtarget",
   142  											UID:  "0",
   143  											GID:  "0",
   144  											Mode: 0666,
   145  										},
   146  									},
   147  								},
   148  							},
   149  						},
   150  					},
   151  				}
   152  			}),
   153  			node: modifyNode(func(n *api.NodeDescription) {
   154  				// use default values
   155  			}),
   156  		},
   157  		{
   158  			desc: "Test expansion of secret not available to task",
   159  			secretSpec: api.SecretSpec{
   160  				Data:       []byte("SECRET_VAL={{secret \"unknowntarget\"}}\n"),
   161  				Templating: &api.Driver{Name: "golang"},
   162  			},
   163  			expectedErr: `failed to expand templated secret templatedsecret: template: expansion:1:13: executing "expansion" at <secret "unknowntarget">: error calling secret: secret target unknowntarget not found`,
   164  			task: modifyTask(func(t *api.Task) {
   165  				t.Spec = api.TaskSpec{
   166  					Runtime: &api.TaskSpec_Container{
   167  						Container: &api.ContainerSpec{
   168  							Secrets: []*api.SecretReference{
   169  								{
   170  									SecretID:   "templatedsecret",
   171  									SecretName: "templatedsecretname",
   172  								},
   173  							},
   174  						},
   175  					},
   176  				}
   177  			}),
   178  			node: modifyNode(func(n *api.NodeDescription) {
   179  				// use default values
   180  			}),
   181  		},
   182  		{
   183  			desc: "Test expansion of config not available to task",
   184  			secretSpec: api.SecretSpec{
   185  				Data:       []byte("CONFIG_VAL={{config \"unknowntarget\"}}\n"),
   186  				Templating: &api.Driver{Name: "golang"},
   187  			},
   188  			expectedErr: `failed to expand templated secret templatedsecret: template: expansion:1:13: executing "expansion" at <config "unknowntarget">: error calling config: config target unknowntarget not found`,
   189  			task: modifyTask(func(t *api.Task) {
   190  				t.Spec = api.TaskSpec{
   191  					Runtime: &api.TaskSpec_Container{
   192  						Container: &api.ContainerSpec{
   193  							Secrets: []*api.SecretReference{
   194  								{
   195  									SecretID:   "templatedsecret",
   196  									SecretName: "templatedsecretname",
   197  								},
   198  							},
   199  						},
   200  					},
   201  				}
   202  			}),
   203  			node: modifyNode(func(n *api.NodeDescription) {
   204  				// use default values
   205  			}),
   206  		},
   207  		{
   208  			desc: "Test that expansion of the same secret avoids recursion",
   209  			secretSpec: api.SecretSpec{
   210  				Data:       []byte("SECRET_VAL={{secret \"templatedsecrettarget\"}}\n"),
   211  				Templating: &api.Driver{Name: "golang"},
   212  			},
   213  			expected: "SECRET_VAL=SECRET_VAL={{secret \"templatedsecrettarget\"}}\n\n",
   214  			task: modifyTask(func(t *api.Task) {
   215  				t.Spec = api.TaskSpec{
   216  					Runtime: &api.TaskSpec_Container{
   217  						Container: &api.ContainerSpec{
   218  							Secrets: []*api.SecretReference{
   219  								{
   220  									SecretID:   "templatedsecret",
   221  									SecretName: "templatedsecretname",
   222  									Target: &api.SecretReference_File{
   223  										File: &api.FileTarget{
   224  											Name: "templatedsecrettarget",
   225  											UID:  "0",
   226  											GID:  "0",
   227  											Mode: 0666,
   228  										},
   229  									},
   230  								},
   231  							},
   232  						},
   233  					},
   234  				}
   235  			}),
   236  			node: modifyNode(func(n *api.NodeDescription) {
   237  				// use default values
   238  			}),
   239  		},
   240  		{
   241  			desc: "Test env",
   242  			secretSpec: api.SecretSpec{
   243  				Data: []byte("ENV VALUE={{env \"foo\"}}\n" +
   244  					"DOES NOT EXIST={{env \"badname\"}}\n"),
   245  				Templating: &api.Driver{Name: "golang"},
   246  			},
   247  			expected: "ENV VALUE=bar\n" +
   248  				"DOES NOT EXIST=\n",
   249  			task: modifyTask(func(t *api.Task) {
   250  				t.Spec = api.TaskSpec{
   251  					Runtime: &api.TaskSpec_Container{
   252  						Container: &api.ContainerSpec{
   253  							Secrets: []*api.SecretReference{
   254  								{
   255  									SecretID:   "templatedsecret",
   256  									SecretName: "templatedsecretname",
   257  								},
   258  							},
   259  							Env: []string{"foo=bar"},
   260  						},
   261  					},
   262  				}
   263  			}),
   264  			node: modifyNode(func(n *api.NodeDescription) {
   265  				// use default values
   266  			}),
   267  		},
   268  	}
   269  
   270  	for _, testCase := range testCases {
   271  		templatedSecret.Spec = testCase.secretSpec
   272  
   273  		dependencyManager := agent.NewDependencyManager()
   274  		dependencyManager.Secrets().Add(*templatedSecret, *referencedSecret)
   275  		dependencyManager.Configs().Add(*referencedConfig)
   276  
   277  		templatedDependencies := NewTemplatedDependencyGetter(agent.Restrict(dependencyManager, testCase.task), testCase.task, testCase.node)
   278  		expandedSecret, err := templatedDependencies.Secrets().Get("templatedsecret")
   279  
   280  		if testCase.expectedErr != "" {
   281  			assert.EqualError(t, err, testCase.expectedErr)
   282  		} else {
   283  			assert.NoError(t, err)
   284  			require.NotNil(t, expandedSecret)
   285  			assert.Equal(t, testCase.expected, string(expandedSecret.Spec.Data), testCase.desc)
   286  		}
   287  	}
   288  }
   289  
   290  func TestTemplatedConfig(t *testing.T) {
   291  	templatedConfig := &api.Config{
   292  		ID: "templatedconfig",
   293  	}
   294  
   295  	referencedSecret := &api.Secret{
   296  		ID: "referencedsecret",
   297  		Spec: api.SecretSpec{
   298  			Data: []byte("mysecret"),
   299  		},
   300  	}
   301  	referencedConfig := &api.Config{
   302  		ID: "referencedconfig",
   303  		Spec: api.ConfigSpec{
   304  			Data: []byte("myconfig"),
   305  		},
   306  	}
   307  
   308  	type testCase struct {
   309  		desc              string
   310  		configSpec        api.ConfigSpec
   311  		task              *api.Task
   312  		expected          string
   313  		expectedErr       string
   314  		expectedSensitive bool
   315  		node              *api.NodeDescription
   316  	}
   317  
   318  	testCases := []testCase{
   319  		{
   320  			desc: "Test expansion of task context",
   321  			configSpec: api.ConfigSpec{
   322  				Data: []byte("SERVICE_ID={{.Service.ID}}\n" +
   323  					"SERVICE_NAME={{.Service.Name}}\n" +
   324  					"TASK_ID={{.Task.ID}}\n" +
   325  					"TASK_NAME={{.Task.Name}}\n" +
   326  					"NODE_ID={{.Node.ID}}\n" +
   327  					"NODE_HOSTNAME={{.Node.Hostname}}\n" +
   328  					"NODE_OS={{.Node.Platform.OS}}\n" +
   329  					"NODE_ARCHITECTURE={{.Node.Platform.Architecture}}"),
   330  				Templating: &api.Driver{Name: "golang"},
   331  			},
   332  			expected: "SERVICE_ID=serviceID\n" +
   333  				"SERVICE_NAME=serviceName\n" +
   334  				"TASK_ID=taskID\n" +
   335  				"TASK_NAME=serviceName.10.taskID\n" +
   336  				"NODE_ID=nodeID\n" +
   337  				"NODE_HOSTNAME=myhostname\n" +
   338  				"NODE_OS=testOS\n" +
   339  				"NODE_ARCHITECTURE=testArchitecture",
   340  			task: modifyTask(func(t *api.Task) {
   341  				t.Spec = api.TaskSpec{
   342  					Runtime: &api.TaskSpec_Container{
   343  						Container: &api.ContainerSpec{
   344  							Configs: []*api.ConfigReference{
   345  								{
   346  									ConfigID:   "templatedconfig",
   347  									ConfigName: "templatedconfigname",
   348  								},
   349  							},
   350  						},
   351  					},
   352  				}
   353  			}),
   354  			node: modifyNode(func(n *api.NodeDescription) {
   355  				n.Hostname = "myhostname"
   356  				n.Platform.OS = "testOS"
   357  				n.Platform.Architecture = "testArchitecture"
   358  			}),
   359  		},
   360  		{
   361  			desc: "Test expansion of secret, by target",
   362  			configSpec: api.ConfigSpec{
   363  				Data:       []byte("SECRET_VAL={{secret \"referencedsecrettarget\"}}\n"),
   364  				Templating: &api.Driver{Name: "golang"},
   365  			},
   366  			expected:          "SECRET_VAL=mysecret\n",
   367  			expectedSensitive: true,
   368  			task: modifyTask(func(t *api.Task) {
   369  				t.Spec = api.TaskSpec{
   370  					Runtime: &api.TaskSpec_Container{
   371  						Container: &api.ContainerSpec{
   372  							Secrets: []*api.SecretReference{
   373  								{
   374  									SecretID:   "referencedsecret",
   375  									SecretName: "referencedsecretname",
   376  									Target: &api.SecretReference_File{
   377  										File: &api.FileTarget{
   378  											Name: "referencedsecrettarget",
   379  											UID:  "0",
   380  											GID:  "0",
   381  											Mode: 0666,
   382  										},
   383  									},
   384  								},
   385  							},
   386  							Configs: []*api.ConfigReference{
   387  								{
   388  									ConfigID:   "templatedconfig",
   389  									ConfigName: "templatedconfigname",
   390  								},
   391  							},
   392  						},
   393  					},
   394  				}
   395  			}),
   396  			node: modifyNode(func(n *api.NodeDescription) {
   397  				// use default values
   398  			}),
   399  		},
   400  		{
   401  			desc: "Test expansion of config, by target",
   402  			configSpec: api.ConfigSpec{
   403  				Data:       []byte("CONFIG_VAL={{config \"referencedconfigtarget\"}}\n"),
   404  				Templating: &api.Driver{Name: "golang"},
   405  			},
   406  			expected: "CONFIG_VAL=myconfig\n",
   407  			task: modifyTask(func(t *api.Task) {
   408  				t.Spec = api.TaskSpec{
   409  					Runtime: &api.TaskSpec_Container{
   410  						Container: &api.ContainerSpec{
   411  							Configs: []*api.ConfigReference{
   412  								{
   413  									ConfigID:   "templatedconfig",
   414  									ConfigName: "templatedconfigname",
   415  								},
   416  								{
   417  									ConfigID:   "referencedconfig",
   418  									ConfigName: "referencedconfigname",
   419  									Target: &api.ConfigReference_File{
   420  										File: &api.FileTarget{
   421  											Name: "referencedconfigtarget",
   422  											UID:  "0",
   423  											GID:  "0",
   424  											Mode: 0666,
   425  										},
   426  									},
   427  								},
   428  							},
   429  						},
   430  					},
   431  				}
   432  			}),
   433  			node: modifyNode(func(n *api.NodeDescription) {
   434  				// use default values
   435  			}),
   436  		},
   437  		{
   438  			desc: "Test expansion of secret not available to task",
   439  			configSpec: api.ConfigSpec{
   440  				Data:       []byte("SECRET_VAL={{secret \"unknowntarget\"}}\n"),
   441  				Templating: &api.Driver{Name: "golang"},
   442  			},
   443  			expectedErr: `failed to expand templated config templatedconfig: template: expansion:1:13: executing "expansion" at <secret "unknowntarget">: error calling secret: secret target unknowntarget not found`,
   444  			task: modifyTask(func(t *api.Task) {
   445  				t.Spec = api.TaskSpec{
   446  					Runtime: &api.TaskSpec_Container{
   447  						Container: &api.ContainerSpec{
   448  							Configs: []*api.ConfigReference{
   449  								{
   450  									ConfigID:   "templatedconfig",
   451  									ConfigName: "templatedconfigname",
   452  								},
   453  							},
   454  						},
   455  					},
   456  				}
   457  			}),
   458  			node: modifyNode(func(n *api.NodeDescription) {
   459  				// use default values
   460  			}),
   461  		},
   462  		{
   463  			desc: "Test expansion of config not available to task",
   464  			configSpec: api.ConfigSpec{
   465  				Data:       []byte("CONFIG_VAL={{config \"unknowntarget\"}}\n"),
   466  				Templating: &api.Driver{Name: "golang"},
   467  			},
   468  			expectedErr: `failed to expand templated config templatedconfig: template: expansion:1:13: executing "expansion" at <config "unknowntarget">: error calling config: config target unknowntarget not found`,
   469  			task: modifyTask(func(t *api.Task) {
   470  				t.Spec = api.TaskSpec{
   471  					Runtime: &api.TaskSpec_Container{
   472  						Container: &api.ContainerSpec{
   473  							Configs: []*api.ConfigReference{
   474  								{
   475  									ConfigID:   "templatedconfig",
   476  									ConfigName: "templatedconfigname",
   477  								},
   478  							},
   479  						},
   480  					},
   481  				}
   482  			}),
   483  			node: modifyNode(func(n *api.NodeDescription) {
   484  				// use default values
   485  			}),
   486  		},
   487  		{
   488  			desc: "Test that expansion of the same config avoids recursion",
   489  			configSpec: api.ConfigSpec{
   490  				Data:       []byte("CONFIG_VAL={{config \"templatedconfigtarget\"}}\n"),
   491  				Templating: &api.Driver{Name: "golang"},
   492  			},
   493  			expected: "CONFIG_VAL=CONFIG_VAL={{config \"templatedconfigtarget\"}}\n\n",
   494  			task: modifyTask(func(t *api.Task) {
   495  				t.Spec = api.TaskSpec{
   496  					Runtime: &api.TaskSpec_Container{
   497  						Container: &api.ContainerSpec{
   498  							Configs: []*api.ConfigReference{
   499  								{
   500  									ConfigID:   "templatedconfig",
   501  									ConfigName: "templatedconfigname",
   502  									Target: &api.ConfigReference_File{
   503  										File: &api.FileTarget{
   504  											Name: "templatedconfigtarget",
   505  											UID:  "0",
   506  											GID:  "0",
   507  											Mode: 0666,
   508  										},
   509  									},
   510  								},
   511  							},
   512  						},
   513  					},
   514  				}
   515  			}),
   516  			node: modifyNode(func(n *api.NodeDescription) {
   517  				// use default values
   518  			}),
   519  		},
   520  		{
   521  			desc: "Test env",
   522  			configSpec: api.ConfigSpec{
   523  				Data: []byte("ENV VALUE={{env \"foo\"}}\n" +
   524  					"DOES NOT EXIST={{env \"badname\"}}\n"),
   525  				Templating: &api.Driver{Name: "golang"},
   526  			},
   527  			expected: "ENV VALUE=bar\n" +
   528  				"DOES NOT EXIST=\n",
   529  			task: modifyTask(func(t *api.Task) {
   530  				t.Spec = api.TaskSpec{
   531  					Runtime: &api.TaskSpec_Container{
   532  						Container: &api.ContainerSpec{
   533  							Configs: []*api.ConfigReference{
   534  								{
   535  									ConfigID:   "templatedconfig",
   536  									ConfigName: "templatedconfigname",
   537  								},
   538  							},
   539  							Env: []string{"foo=bar"},
   540  						},
   541  					},
   542  				}
   543  			}),
   544  			node: modifyNode(func(n *api.NodeDescription) {
   545  				// use default values
   546  			}),
   547  		},
   548  	}
   549  
   550  	for _, testCase := range testCases {
   551  		templatedConfig.Spec = testCase.configSpec
   552  
   553  		dependencyManager := agent.NewDependencyManager()
   554  		dependencyManager.Configs().Add(*templatedConfig, *referencedConfig)
   555  		dependencyManager.Secrets().Add(*referencedSecret)
   556  
   557  		templatedDependencies := NewTemplatedDependencyGetter(agent.Restrict(dependencyManager, testCase.task), testCase.task, testCase.node)
   558  		expandedConfig1, err1 := templatedDependencies.Configs().Get("templatedconfig")
   559  		expandedConfig2, sensitive, err2 := templatedDependencies.Configs().(TemplatedConfigGetter).GetAndFlagSecretData("templatedconfig")
   560  
   561  		if testCase.expectedErr != "" {
   562  			assert.EqualError(t, err1, testCase.expectedErr)
   563  			assert.EqualError(t, err2, testCase.expectedErr)
   564  		} else {
   565  			assert.NoError(t, err1)
   566  			assert.NoError(t, err2)
   567  			require.NotNil(t, expandedConfig1)
   568  			require.NotNil(t, expandedConfig2)
   569  			assert.Equal(t, testCase.expected, string(expandedConfig1.Spec.Data), testCase.desc)
   570  			assert.Equal(t, testCase.expected, string(expandedConfig2.Spec.Data), testCase.desc)
   571  			assert.Equal(t, testCase.expectedSensitive, sensitive, testCase.desc)
   572  		}
   573  	}
   574  }