github.com/nilium/gitlab-runner@v12.5.0+incompatible/commands/register_test.go (about) 1 package commands 2 3 import ( 4 "bytes" 5 "flag" 6 "fmt" 7 "io/ioutil" 8 "os" 9 "testing" 10 11 "github.com/imdario/mergo" 12 "github.com/pkg/errors" 13 "github.com/sirupsen/logrus" 14 "github.com/sirupsen/logrus/hooks/test" 15 "github.com/stretchr/testify/assert" 16 "github.com/stretchr/testify/mock" 17 "github.com/stretchr/testify/require" 18 "github.com/urfave/cli" 19 "gitlab.com/ayufan/golang-cli-helpers" 20 21 "gitlab.com/gitlab-org/gitlab-runner/common" 22 ) 23 24 func setupDockerRegisterCommand(dockerConfig *common.DockerConfig) *RegisterCommand { 25 fs := flag.NewFlagSet("", flag.ExitOnError) 26 ctx := cli.NewContext(cli.NewApp(), fs, nil) 27 fs.String("docker-image", "ruby:2.1", "") 28 29 s := &RegisterCommand{ 30 context: ctx, 31 NonInteractive: true, 32 } 33 s.Docker = dockerConfig 34 35 return s 36 } 37 38 func TestRegisterDefaultDockerCacheVolume(t *testing.T) { 39 s := setupDockerRegisterCommand(&common.DockerConfig{ 40 Volumes: []string{}, 41 }) 42 43 s.askDocker() 44 45 assert.Equal(t, 1, len(s.Docker.Volumes)) 46 assert.Equal(t, "/cache", s.Docker.Volumes[0]) 47 } 48 49 func TestRegisterCustomDockerCacheVolume(t *testing.T) { 50 s := setupDockerRegisterCommand(&common.DockerConfig{ 51 Volumes: []string{"/cache"}, 52 }) 53 54 s.askDocker() 55 56 assert.Equal(t, 1, len(s.Docker.Volumes)) 57 assert.Equal(t, "/cache", s.Docker.Volumes[0]) 58 } 59 60 func TestRegisterCustomMappedDockerCacheVolume(t *testing.T) { 61 s := setupDockerRegisterCommand(&common.DockerConfig{ 62 Volumes: []string{"/my/cache:/cache"}, 63 }) 64 65 s.askDocker() 66 67 assert.Equal(t, 1, len(s.Docker.Volumes)) 68 assert.Equal(t, "/my/cache:/cache", s.Docker.Volumes[0]) 69 } 70 71 func getLogrusOutput(t *testing.T, hook *test.Hook) string { 72 buf := &bytes.Buffer{} 73 for _, entry := range hook.AllEntries() { 74 message, err := entry.String() 75 require.NoError(t, err) 76 77 buf.WriteString(message) 78 } 79 80 return buf.String() 81 } 82 83 func testRegisterCommandRun(t *testing.T, network common.Network, args ...string) (content string, output string, err error) { 84 hook := test.NewGlobal() 85 86 defer func() { 87 output = getLogrusOutput(t, hook) 88 89 if r := recover(); r != nil { 90 // log panics forces exit 91 if e, ok := r.(*logrus.Entry); ok { 92 err = fmt.Errorf("command error: %s", e.Message) 93 } 94 } 95 }() 96 97 cmd := newRegisterCommand() 98 cmd.network = network 99 100 app := cli.NewApp() 101 app.Commands = []cli.Command{ 102 { 103 Name: "register", 104 Action: cmd.Execute, 105 Flags: clihelpers.GetFlagsFromStruct(cmd), 106 }, 107 } 108 109 configFile, err := ioutil.TempFile("", "config.toml") 110 require.NoError(t, err) 111 112 err = configFile.Close() 113 require.NoError(t, err) 114 115 defer os.Remove(configFile.Name()) 116 117 args = append([]string{ 118 "binary", "register", 119 "-n", 120 "--config", configFile.Name(), 121 "--url", "http://gitlab.example.com/", 122 "--registration-token", "test-registration-token", 123 "--executor", "shell", 124 }, args...) 125 126 comandErr := app.Run(args) 127 128 fileContent, err := ioutil.ReadFile(configFile.Name()) 129 require.NoError(t, err) 130 131 err = comandErr 132 133 return string(fileContent), "", err 134 } 135 136 func TestAccessLevelSetting(t *testing.T) { 137 tests := map[string]struct { 138 accessLevel AccessLevel 139 failureExpected bool 140 }{ 141 "access level not defined": {}, 142 "ref_protected used": { 143 accessLevel: RefProtected, 144 }, 145 "not_protected used": { 146 accessLevel: NotProtected, 147 }, 148 "unknown access level": { 149 accessLevel: AccessLevel("unknown"), 150 failureExpected: true, 151 }, 152 } 153 154 for testName, testCase := range tests { 155 t.Run(testName, func(t *testing.T) { 156 network := new(common.MockNetwork) 157 defer network.AssertExpectations(t) 158 159 if !testCase.failureExpected { 160 parametersMocker := mock.MatchedBy(func(parameters common.RegisterRunnerParameters) bool { 161 return AccessLevel(parameters.AccessLevel) == testCase.accessLevel 162 }) 163 164 network.On("RegisterRunner", mock.Anything, parametersMocker). 165 Return(&common.RegisterRunnerResponse{ 166 Token: "test-runner-token", 167 }). 168 Once() 169 } 170 171 arguments := []string{ 172 "--access-level", string(testCase.accessLevel), 173 } 174 175 _, output, err := testRegisterCommandRun(t, network, arguments...) 176 177 if testCase.failureExpected { 178 assert.EqualError(t, err, "command error: Given access-level is not valid. Please refer to gitlab-runner register -h for the correct options.") 179 assert.NotContains(t, output, "Runner registered successfully.") 180 181 return 182 } 183 184 assert.NoError(t, err) 185 assert.Contains(t, output, "Runner registered successfully.") 186 }) 187 } 188 } 189 190 func TestConfigTemplate_Enabled(t *testing.T) { 191 tests := map[string]struct { 192 path string 193 expectedValue bool 194 }{ 195 "configuration file defined": { 196 path: "/path/to/file", 197 expectedValue: true, 198 }, 199 "configuration file not defined": { 200 path: "", 201 expectedValue: false, 202 }, 203 } 204 205 for tn, tc := range tests { 206 t.Run(tn, func(t *testing.T) { 207 configTemplate := &configTemplate{ConfigFile: tc.path} 208 assert.Equal(t, tc.expectedValue, configTemplate.Enabled()) 209 }) 210 } 211 } 212 213 func prepareConfigurationTemplateFile(t *testing.T, content string) (string, func()) { 214 file, err := ioutil.TempFile("", "config.template.toml") 215 require.NoError(t, err) 216 217 defer func() { 218 err = file.Close() 219 require.NoError(t, err) 220 }() 221 222 _, err = file.WriteString(content) 223 require.NoError(t, err) 224 225 cleanup := func() { 226 _ = os.Remove(file.Name()) 227 } 228 229 return file.Name(), cleanup 230 } 231 232 var ( 233 configTemplateMergeToInvalidConfiguration = `- , ;` 234 235 configTemplateMergeToEmptyConfiguration = `` 236 237 configTemplateMergeToTwoRunnerSectionsConfiguration = ` 238 [[runners]] 239 [[runners]]` 240 241 configTemplateMergeToOverwritingConfiguration = ` 242 [[runners]] 243 token = "different_token" 244 executor = "docker" 245 limit = 100` 246 247 configTemplateMergeToAdditionalConfiguration = ` 248 [[runners]] 249 [runners.kubernetes] 250 [runners.kubernetes.volumes] 251 [[runners.kubernetes.volumes.empty_dir]] 252 name = "empty_dir" 253 mount_path = "/path/to/empty_dir" 254 medium = "Memory"` 255 256 configTemplateMergeToBaseConfiguration = &common.RunnerConfig{ 257 RunnerCredentials: common.RunnerCredentials{ 258 Token: "test-runner-token", 259 }, 260 RunnerSettings: common.RunnerSettings{ 261 Executor: "shell", 262 }, 263 } 264 ) 265 266 func TestConfigTemplate_MergeTo(t *testing.T) { 267 tests := map[string]struct { 268 templateContent string 269 config *common.RunnerConfig 270 271 expectedError error 272 assertConfiguration func(t *testing.T, config *common.RunnerConfig) 273 }{ 274 "invalid template file": { 275 templateContent: configTemplateMergeToInvalidConfiguration, 276 config: configTemplateMergeToBaseConfiguration, 277 expectedError: errors.New("couldn't load configuration template file: Near line 1 (last key parsed '-'): expected key separator '=', but got ',' instead"), 278 }, 279 "no runners in template": { 280 templateContent: configTemplateMergeToEmptyConfiguration, 281 config: configTemplateMergeToBaseConfiguration, 282 expectedError: errors.New("configuration template must contain exactly one [[runners]] entry"), 283 }, 284 "multiple runners in template": { 285 templateContent: configTemplateMergeToTwoRunnerSectionsConfiguration, 286 config: configTemplateMergeToBaseConfiguration, 287 expectedError: errors.New("configuration template must contain exactly one [[runners]] entry"), 288 }, 289 "template doesn't overwrite existing settings": { 290 templateContent: configTemplateMergeToOverwritingConfiguration, 291 config: configTemplateMergeToBaseConfiguration, 292 assertConfiguration: func(t *testing.T, config *common.RunnerConfig) { 293 assert.Equal(t, configTemplateMergeToBaseConfiguration.Token, config.RunnerCredentials.Token) 294 assert.Equal(t, configTemplateMergeToBaseConfiguration.Executor, config.RunnerSettings.Executor) 295 assert.Equal(t, 100, config.Limit) 296 }, 297 expectedError: nil, 298 }, 299 "template adds additional content": { 300 templateContent: configTemplateMergeToAdditionalConfiguration, 301 config: configTemplateMergeToBaseConfiguration, 302 assertConfiguration: func(t *testing.T, config *common.RunnerConfig) { 303 k8s := config.RunnerSettings.Kubernetes 304 305 require.NotNil(t, k8s) 306 require.NotEmpty(t, k8s.Volumes.EmptyDirs) 307 assert.Len(t, k8s.Volumes.EmptyDirs, 1) 308 309 emptyDir := k8s.Volumes.EmptyDirs[0] 310 assert.Equal(t, "empty_dir", emptyDir.Name) 311 assert.Equal(t, "/path/to/empty_dir", emptyDir.MountPath) 312 assert.Equal(t, "Memory", emptyDir.Medium) 313 }, 314 expectedError: nil, 315 }, 316 "error on merging": { 317 templateContent: configTemplateMergeToAdditionalConfiguration, 318 expectedError: errors.Wrap(mergo.ErrNotSupported, "error while merging configuration with configuration template"), 319 }, 320 } 321 322 for tn, tc := range tests { 323 t.Run(tn, func(t *testing.T) { 324 file, cleanup := prepareConfigurationTemplateFile(t, tc.templateContent) 325 defer cleanup() 326 327 configTemplate := &configTemplate{ConfigFile: file} 328 err := configTemplate.MergeTo(tc.config) 329 330 if tc.expectedError != nil { 331 assert.EqualError(t, err, tc.expectedError.Error()) 332 333 return 334 } 335 336 assert.NoError(t, err) 337 tc.assertConfiguration(t, tc.config) 338 }) 339 } 340 }