github.com/Axway/agent-sdk@v1.1.101/pkg/cmd/cmd_test.go (about) 1 package cmd 2 3 import ( 4 "bufio" 5 "bytes" 6 "encoding/json" 7 "errors" 8 "io/ioutil" 9 "net/http" 10 "net/http/httptest" 11 "os" 12 "strings" 13 "testing" 14 "time" 15 16 "github.com/Axway/agent-sdk/pkg/apic/definitions" 17 "github.com/Axway/agent-sdk/pkg/cmd/properties" 18 19 "github.com/spf13/cobra" 20 flag "github.com/spf13/pflag" 21 "github.com/spf13/viper" 22 "github.com/stretchr/testify/assert" 23 24 v1 "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/api/v1" 25 management "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/management/v1alpha1" 26 corecfg "github.com/Axway/agent-sdk/pkg/config" 27 ) 28 29 func getPFlag(cmd AgentRootCmd, flagName string) *flag.Flag { 30 return cmd.RootCmd().Flags().Lookup(flagName) 31 } 32 33 func assertCmdFlag(t *testing.T, cmd AgentRootCmd, flagName, fType, description string) { 34 pflag := getPFlag(cmd, flagName) 35 assert.NotNil(t, &pflag) 36 assert.Equal(t, fType, pflag.Value.Type()) 37 assert.Equal(t, description, pflag.Usage) 38 } 39 40 func assertStringCmdFlag(t *testing.T, cmd AgentRootCmd, propertyName, flagName, defaultVal, description string) { 41 assertCmdFlag(t, cmd, flagName, "string", description) 42 assert.Equal(t, defaultVal, viper.GetString(propertyName)) 43 } 44 45 func assertStringSliceCmdFlag(t *testing.T, cmd AgentRootCmd, propertyName, flagName string, defaultVal []string, description string) { 46 assertCmdFlag(t, cmd, flagName, "stringSlice", description) 47 assert.Equal(t, defaultVal, viper.GetStringSlice(propertyName)) 48 } 49 50 func assertBooleanCmdFlag(t *testing.T, cmd AgentRootCmd, propertyName, flagName string, defaultVal bool, description string) { 51 assertCmdFlag(t, cmd, flagName, "bool", description) 52 assert.Equal(t, defaultVal, viper.GetBool(propertyName)) 53 } 54 55 func assertDurationCmdFlag(t *testing.T, cmd AgentRootCmd, propertyName, flagName string, defaultVal time.Duration, description string) { 56 assertCmdFlag(t, cmd, flagName, "duration", description) 57 assert.Equal(t, defaultVal, viper.GetDuration(propertyName)) 58 } 59 60 type agentConfig struct { 61 bProp bool 62 dProp time.Duration 63 iProp int 64 sProp string 65 sPropExt string 66 ssProp []string 67 agentValidationCalled bool 68 } 69 70 func (a *agentConfig) ValidateCfg() error { 71 a.agentValidationCalled = true 72 if a.sProp == "" { 73 return errors.New("agentConfig: String prop not set") 74 } 75 return nil 76 } 77 78 type configWithValidation struct { 79 configValidationCalled bool 80 CentralCfg corecfg.CentralConfig 81 AgentCfg *agentConfig 82 } 83 84 func (c *configWithValidation) ValidateCfg() error { 85 c.configValidationCalled = true 86 if c.AgentCfg.sProp == "" { 87 return errors.New("configWithValidation: String prop not set") 88 } 89 return nil 90 } 91 92 type configWithNoValidation struct { 93 configValidationCalled bool 94 CentralCfg corecfg.CentralConfig 95 AgentCfg corecfg.IConfigValidator 96 } 97 98 func TestRootCmdFlags(t *testing.T) { 99 // Discovery Agent 100 rootCmd := NewRootCmd("Test", "TestRootCmd", nil, nil, corecfg.DiscoveryAgent) 101 assertStringCmdFlag(t, rootCmd, "central.url", "centralUrl", "", "URL of Amplify Central") // assert to empty "" - set by region settings 102 assertStringCmdFlag(t, rootCmd, "central.platformURL", "centralPlatformURL", "", "URL of the platform") // assert to empty "" - set by region settings 103 assertStringCmdFlag(t, rootCmd, "central.singleURL", "centralSingleURL", "", "Alternate Connection for Agent if using static IP") 104 assertStringCmdFlag(t, rootCmd, "central.organizationID", "centralOrganizationID", "", "Tenant ID for the owner of the environment") 105 assertStringCmdFlag(t, rootCmd, "central.team", "centralTeam", "", "Team name for creating catalog") 106 assertStringCmdFlag(t, rootCmd, "central.environment", "centralEnvironment", "", "The Environment that the APIs will be associated with in Amplify Central") 107 assertStringCmdFlag(t, rootCmd, "central.auth.privateKey", "centralAuthPrivateKey", "/etc/private_key.pem", "Path to the private key for Amplify Central Authentication") 108 assertStringCmdFlag(t, rootCmd, "central.auth.publicKey", "centralAuthPublicKey", "/etc/public_key", "Path to the public key for Amplify Central Authentication") 109 assertStringCmdFlag(t, rootCmd, "central.auth.word", "centralAuthKeyPassword", "", "Path to the password file required by the private key for Amplify Central Authentication") 110 assertStringCmdFlag(t, rootCmd, "central.auth.url", "centralAuthUrl", "", "Amplify Central authentication URL") // assert to empty "" - set by region settings 111 assertStringCmdFlag(t, rootCmd, "central.auth.realm", "centralAuthRealm", "Broker", "Amplify Central authentication Realm") 112 assertStringCmdFlag(t, rootCmd, "central.auth.clientId", "centralAuthClientId", "", "Client ID for the service account") 113 assertDurationCmdFlag(t, rootCmd, "central.auth.timeout", "centralAuthTimeout", 10*time.Second, "Timeout waiting for AxwayID response") 114 assertStringSliceCmdFlag(t, rootCmd, "central.ssl.nextProtos", "centralSslNextProtos", []string{}, "List of supported application level protocols, comma separated") 115 assertBooleanCmdFlag(t, rootCmd, "central.ssl.insecureSkipVerify", "centralSslInsecureSkipVerify", false, "Controls whether a client verifies the server's certificate chain and host name") 116 assertStringSliceCmdFlag(t, rootCmd, "central.ssl.cipherSuites", "centralSslCipherSuites", corecfg.TLSDefaultCipherSuitesStringSlice(), "List of supported cipher suites, comma separated") 117 assertStringCmdFlag(t, rootCmd, "central.ssl.minVersion", "centralSslMinVersion", corecfg.TLSDefaultMinVersionString(), "Minimum acceptable SSL/TLS protocol version") 118 assertStringCmdFlag(t, rootCmd, "central.ssl.maxVersion", "centralSslMaxVersion", "0", "Maximum acceptable SSL/TLS protocol version") 119 assertBooleanCmdFlag(t, rootCmd, "central.migration.cleanInstances", "centralMigrationCleanInstances", false, "Set this to clean all but latest instance, per stage, within an API Service") 120 121 // Traceability Agent 122 rootCmd = NewRootCmd("Test", "TestRootCmd", nil, nil, corecfg.TraceabilityAgent) 123 assertStringCmdFlag(t, rootCmd, "central.deployment", "centralDeployment", "", "Amplify Central") // assert to empty "" - set by region settings 124 assertStringCmdFlag(t, rootCmd, "central.url", "centralUrl", "", "URL of Amplify Central") // assert to empty "" - set by region settings 125 assertStringCmdFlag(t, rootCmd, "central.platformURL", "centralPlatformURL", "", "URL of the platform") // assert to empty "" - set by region settings 126 assertStringCmdFlag(t, rootCmd, "central.singleURL", "centralSingleURL", "", "Alternate Connection for Agent if using static IP") 127 assertStringCmdFlag(t, rootCmd, "central.organizationID", "centralOrganizationID", "", "Tenant ID for the owner of the environment") 128 assertStringCmdFlag(t, rootCmd, "central.auth.privateKey", "centralAuthPrivateKey", "/etc/private_key.pem", "Path to the private key for Amplify Central Authentication") 129 assertStringCmdFlag(t, rootCmd, "central.auth.publicKey", "centralAuthPublicKey", "/etc/public_key", "Path to the public key for Amplify Central Authentication") 130 assertStringCmdFlag(t, rootCmd, "central.auth.keyPassword", "centralAuthKeyPassword", "", "Path to the password file required by the private key for Amplify Central Authentication") 131 assertStringCmdFlag(t, rootCmd, "central.auth.url", "centralAuthUrl", "", "Amplify Central authentication URL") // assert to empty "" - set by region settings 132 assertStringCmdFlag(t, rootCmd, "central.auth.realm", "centralAuthRealm", "Broker", "Amplify Central authentication Realm") 133 assertStringCmdFlag(t, rootCmd, "central.auth.clientId", "centralAuthClientId", "", "Client ID for the service account") 134 assertDurationCmdFlag(t, rootCmd, "central.auth.timeout", "centralAuthTimeout", 10*time.Second, "Timeout waiting for AxwayID response") 135 assertStringSliceCmdFlag(t, rootCmd, "central.ssl.nextProtos", "centralSslNextProtos", []string{}, "List of supported application level protocols, comma separated") 136 assertBooleanCmdFlag(t, rootCmd, "central.ssl.insecureSkipVerify", "centralSslInsecureSkipVerify", false, "Controls whether a client verifies the server's certificate chain and host name") 137 assertStringSliceCmdFlag(t, rootCmd, "central.ssl.cipherSuites", "centralSslCipherSuites", corecfg.TLSDefaultCipherSuitesStringSlice(), "List of supported cipher suites, comma separated") 138 assertStringCmdFlag(t, rootCmd, "central.ssl.minVersion", "centralSslMinVersion", corecfg.TLSDefaultMinVersionString(), "Minimum acceptable SSL/TLS protocol version") 139 assertStringCmdFlag(t, rootCmd, "central.ssl.maxVersion", "centralSslMaxVersion", "0", "Maximum acceptable SSL/TLS protocol version") 140 141 // Log yaml properties and command flags 142 assertStringCmdFlag(t, rootCmd, "log.level", "logLevel", "info", "Log level (trace, debug, info, warn, error)") 143 assertStringCmdFlag(t, rootCmd, "log.format", "logFormat", "json", "Log format (json, line)") 144 assertStringCmdFlag(t, rootCmd, "log.output", "logOutput", "stdout", "Log output type (stdout, file, both)") 145 assertStringCmdFlag(t, rootCmd, "log.file.path", "logFilePath", "logs", "Log file path if output type is file or both") 146 } 147 148 func TestNewCmd(t *testing.T) { 149 rootCmd := &cobra.Command{} 150 newCmd := NewCmd( 151 rootCmd, 152 "test", 153 "discovery agent", 154 func(centralConfig corecfg.CentralConfig) (interface{}, error) { 155 return nil, nil 156 }, 157 func() error { 158 return nil 159 }, 160 corecfg.DiscoveryAgent, 161 ) 162 163 assert.NotNil(t, newCmd) 164 165 } 166 167 func TestRootCmdConfigFileLoad(t *testing.T) { 168 169 rootCmd := NewRootCmd("Test", "TestRootCmd", nil, nil, corecfg.DiscoveryAgent) 170 171 err := rootCmd.Execute() 172 173 // should be FileNotFound error 174 assert.NotNil(t, err, err.Error()) 175 if err != nil { 176 _, ok := err.(viper.ConfigFileNotFoundError) 177 assert.True(t, ok, "Incorrect error returned: %s", err.Error()) 178 } 179 180 rootCmd = NewRootCmd("test_no_overide", "test_no_overide", nil, nil, corecfg.DiscoveryAgent) 181 viper.AddConfigPath("./testdata") 182 err = rootCmd.Execute() 183 184 // should NOT be FileNotFound error 185 assert.NotNil(t, err, err.Error()) 186 if err != nil { 187 _, ok := err.(viper.ConfigFileNotFoundError) 188 assert.False(t, ok, "Incorrect error returned: %s", err.Error()) 189 } 190 191 errBuf := new(bytes.Buffer) 192 rootCmd.RootCmd().SetErr(errBuf) 193 194 assert.Contains(t, "Error central.organizationID not set in config", errBuf.String()) 195 } 196 197 func TestRootCmdConfigDefault(t *testing.T) { 198 discoveryInitConfigHandler := func(centralConfig corecfg.CentralConfig) (interface{}, error) { 199 assert.Equal(t, "https://apicentral.axway.com", centralConfig.GetURL()) 200 assert.Equal(t, "222222", centralConfig.GetTeamName()) 201 assert.Equal(t, "https://login.axway.com/auth/realms/Broker", centralConfig.GetAuthConfig().GetAudience()) 202 assert.Equal(t, "https://login.axway.com/auth/realms/Broker/protocol/openid-connect/token", centralConfig.GetAuthConfig().GetTokenURL()) 203 assert.Equal(t, "cccc", centralConfig.GetAuthConfig().GetClientID()) 204 assert.Equal(t, "Broker", centralConfig.GetAuthConfig().GetRealm()) 205 assert.Equal(t, "/etc/private_key.pem", centralConfig.GetAuthConfig().GetPrivateKey()) 206 assert.Equal(t, "/etc/public_key", centralConfig.GetAuthConfig().GetPublicKey()) 207 assert.Equal(t, "", centralConfig.GetAuthConfig().GetKeyPassword()) 208 assert.Equal(t, 10*time.Second, centralConfig.GetAuthConfig().GetTimeout()) 209 return centralConfig, errors.New("Test return error from init config handler") 210 } 211 212 traceabilityInitConfigHandler := func(centralConfig corecfg.CentralConfig) (interface{}, error) { 213 assert.Equal(t, "prod", centralConfig.GetAPICDeployment()) 214 assert.Equal(t, "https://login.axway.com/auth/realms/Broker", centralConfig.GetAuthConfig().GetAudience()) 215 assert.Equal(t, "https://login.axway.com/auth/realms/Broker/protocol/openid-connect/token", centralConfig.GetAuthConfig().GetTokenURL()) 216 assert.Equal(t, "cccc", centralConfig.GetAuthConfig().GetClientID()) 217 assert.Equal(t, "Broker", centralConfig.GetAuthConfig().GetRealm()) 218 assert.Equal(t, "/etc/private_key.pem", centralConfig.GetAuthConfig().GetPrivateKey()) 219 assert.Equal(t, "/etc/public_key", centralConfig.GetAuthConfig().GetPublicKey()) 220 assert.Equal(t, "", centralConfig.GetAuthConfig().GetKeyPassword()) 221 assert.Equal(t, 10*time.Second, centralConfig.GetAuthConfig().GetTimeout()) 222 return centralConfig, errors.New("Test return error from init config handler") 223 } 224 225 // Discovery 226 rootCmd := NewRootCmd("test_with_non_defaults", "test_with_non_defaults", discoveryInitConfigHandler, nil, corecfg.DiscoveryAgent) 227 viper.AddConfigPath("./testdata") 228 err := rootCmd.Execute() 229 230 // should NOT be FileNotFound error 231 assert.NotNil(t, err, err.Error()) 232 if err != nil { 233 _, ok := err.(viper.ConfigFileNotFoundError) 234 assert.False(t, ok, "Incorrect error returned: %s", err.Error()) 235 } 236 237 errBuf := new(bytes.Buffer) 238 rootCmd.RootCmd().SetErr(errBuf) 239 assert.Contains(t, "Test return error from init config handler, Discovery Agent", errBuf.String()) 240 241 // Traceability 242 rootCmd = NewRootCmd("test_with_non_defaults", "test_with_non_defaults", traceabilityInitConfigHandler, nil, corecfg.TraceabilityAgent) 243 viper.AddConfigPath("./testdata") 244 err = rootCmd.Execute() 245 246 // should NOT be FileNotFound error 247 assert.NotNil(t, err, err.Error()) 248 if err != nil { 249 _, ok := err.(viper.ConfigFileNotFoundError) 250 assert.False(t, ok, "Incorrect error returned: %s", err.Error()) 251 } 252 253 errBuf = new(bytes.Buffer) 254 rootCmd.RootCmd().SetErr(errBuf) 255 assert.Contains(t, "Test return error from init config handler, Traceability Agent", errBuf.String()) 256 } 257 258 func TestRootCmdAgentConfigValidation(t *testing.T) { 259 s := newTestServer() 260 defer s.Close() 261 262 var rootCmd AgentRootCmd 263 var cfg *configWithValidation 264 initConfigHandler := func(centralConfig corecfg.CentralConfig) (interface{}, error) { 265 cfg = &configWithValidation{ 266 configValidationCalled: false, 267 CentralCfg: centralConfig, 268 AgentCfg: &agentConfig{ 269 agentValidationCalled: false, 270 bProp: rootCmd.GetProperties().BoolPropertyValue("agent.bool"), 271 dProp: rootCmd.GetProperties().DurationPropertyValue("agent.duration"), 272 iProp: rootCmd.GetProperties().IntPropertyValue("agent.int"), 273 sProp: rootCmd.GetProperties().StringPropertyValue("agent.string"), 274 ssProp: rootCmd.GetProperties().StringSlicePropertyValue("agent.stringSlice"), 275 }, 276 } 277 return cfg, nil 278 } 279 280 os.Setenv("CENTRAL_AUTH_PRIVATEKEY", "../transaction/testdata/private_key.pem") 281 os.Setenv("CENTRAL_AUTH_PUBLICKEY", "../transaction/testdata/public_key") 282 os.Setenv("CENTRAL_AUTH_CLIENTID", "serviceaccount_1234") 283 os.Setenv("CENTRAL_AUTH_URL", s.URL) 284 os.Setenv("CENTRAL_URL", s.URL) 285 os.Setenv("CENTRAL_SINGLEURL", s.URL) 286 os.Setenv("CENTRAL_PLATFORMURL", s.URL) 287 288 rootCmd = NewRootCmd("test_with_non_defaults", "test_with_non_defaults", initConfigHandler, nil, corecfg.DiscoveryAgent) 289 viper.AddConfigPath("./testdata") 290 291 rootCmd.GetProperties().AddBoolProperty("agent.bool", false, "Agent Bool Property") 292 rootCmd.GetProperties().AddDurationProperty("agent.duration", 10*time.Second, "Agent Duration Property", properties.WithLowerLimit(10*time.Second)) 293 rootCmd.GetProperties().AddIntProperty("agent.int", 0, "Agent Int Property") 294 rootCmd.GetProperties().AddStringProperty("agent.string", "", "Agent String Property") 295 rootCmd.GetProperties().AddStringSliceProperty("agent.stringSlice", nil, "Agent String Slice Property") 296 297 err := rootCmd.Execute() 298 299 // should NOT be FileNotFound error 300 assert.NotNil(t, err, err.Error()) 301 if err != nil { 302 _, ok := err.(viper.ConfigFileNotFoundError) 303 assert.False(t, ok, "Incorrect error returned: %s", err.Error()) 304 } 305 306 errBuf := new(bytes.Buffer) 307 rootCmd.RootCmd().SetErr(errBuf) 308 assert.Contains(t, "configWithValidation: String prop not set", errBuf.String()) 309 assert.Equal(t, true, cfg.configValidationCalled) 310 assert.Equal(t, false, cfg.AgentCfg.agentValidationCalled) 311 } 312 313 func TestRootCmdAgentConfigChildValidation(t *testing.T) { 314 s := newTestServer() 315 defer s.Close() 316 317 var rootCmd AgentRootCmd 318 var cfg *configWithNoValidation 319 initConfigHandler := func(centralConfig corecfg.CentralConfig) (interface{}, error) { 320 cfg = &configWithNoValidation{ 321 configValidationCalled: false, 322 CentralCfg: centralConfig, 323 AgentCfg: &agentConfig{ 324 agentValidationCalled: false, 325 bProp: rootCmd.GetProperties().BoolPropertyValue("agent.bool"), 326 dProp: rootCmd.GetProperties().DurationPropertyValue("agent.duration"), 327 iProp: rootCmd.GetProperties().IntPropertyValue("agent.int"), 328 sProp: rootCmd.GetProperties().StringPropertyValue("agent.string"), 329 ssProp: rootCmd.GetProperties().StringSlicePropertyValue("agent.stringSlice"), 330 }, 331 } 332 return cfg, nil 333 } 334 335 os.Setenv("CENTRAL_AUTH_PRIVATEKEY", "../transaction/testdata/private_key.pem") 336 os.Setenv("CENTRAL_AUTH_PUBLICKEY", "../transaction/testdata/public_key") 337 os.Setenv("CENTRAL_AUTH_CLIENTID", "serviceaccount_1234") 338 os.Setenv("CENTRAL_AUTH_URL", s.URL) 339 os.Setenv("CENTRAL_URL", s.URL) 340 os.Setenv("CENTRAL_SINGLEURL", s.URL) 341 342 rootCmd = NewRootCmd("test_with_non_defaults", "test_with_non_defaults", initConfigHandler, nil, corecfg.DiscoveryAgent) 343 viper.AddConfigPath("./testdata") 344 345 rootCmd.GetProperties().AddBoolProperty("agent.bool", false, "Agent Bool Property") 346 rootCmd.GetProperties().AddDurationProperty("agent.duration", 10*time.Second, "Agent Duration Property", properties.WithLowerLimit(10*time.Second)) 347 rootCmd.GetProperties().AddIntProperty("agent.int", 0, "Agent Int Property") 348 rootCmd.GetProperties().AddStringProperty("agent.string", "", "Agent String Property") 349 rootCmd.GetProperties().AddStringSliceProperty("agent.stringSlice", nil, "Agent String Slice Property") 350 351 err := rootCmd.Execute() 352 353 // should NOT be FileNotFound error 354 assert.NotNil(t, err, err.Error()) 355 if err != nil { 356 _, ok := err.(viper.ConfigFileNotFoundError) 357 assert.False(t, ok, "Incorrect error returned: %s", err.Error()) 358 } 359 360 errBuf := new(bytes.Buffer) 361 rootCmd.RootCmd().SetErr(errBuf) 362 assert.Contains(t, "agentConfig: String prop not set", errBuf.String()) 363 assert.Equal(t, false, cfg.configValidationCalled) 364 assert.Equal(t, true, cfg.AgentCfg.(*agentConfig).agentValidationCalled) 365 } 366 367 func TestRootCmdHandlersWithError(t *testing.T) { 368 s := newTestServer() 369 defer s.Close() 370 371 initConfigHandler := func(centralConfig corecfg.CentralConfig) (interface{}, error) { 372 return centralConfig, nil 373 } 374 cmdHandler := func() error { 375 return nil 376 } 377 378 os.Setenv("CENTRAL_AUTH_PRIVATEKEY", "../transaction/testdata/private_key.pem") 379 os.Setenv("CENTRAL_AUTH_PUBLICKEY", "../transaction/testdata/public_key") 380 os.Setenv("CENTRAL_AUTH_CLIENTID", "serviceaccount_1234") 381 os.Setenv("CENTRAL_AUTH_URL", s.URL) 382 os.Setenv("CENTRAL_URL", s.URL) 383 os.Setenv("CENTRAL_SINGLEURL", s.URL) 384 385 rootCmd := NewRootCmd("Test", "TestRootCmd", initConfigHandler, cmdHandler, corecfg.DiscoveryAgent) 386 err := rootCmd.Execute() 387 388 // should be FileNotFound error 389 assert.NotNil(t, err, err.Error()) 390 if err != nil { 391 _, ok := err.(viper.ConfigFileNotFoundError) 392 assert.True(t, ok, "Incorrect error returned: %s", err.Error()) 393 } 394 395 rootCmd = NewRootCmd("test_no_overide", "test_no_overide", initConfigHandler, cmdHandler, corecfg.DiscoveryAgent) 396 viper.AddConfigPath("./testdata") 397 err = rootCmd.Execute() 398 399 // should NOT be FileNotFound error 400 assert.NotNil(t, err, err.Error()) 401 if err != nil { 402 _, ok := err.(viper.ConfigFileNotFoundError) 403 assert.False(t, ok, "Incorrect error returned: %s", err.Error()) 404 } 405 } 406 407 func TestRootCmdHandlers(t *testing.T) { 408 s := newTestServer() 409 defer s.Close() 410 411 var rootCmd AgentRootCmd 412 var cfg *configWithNoValidation 413 initConfigHandler := func(centralConfig corecfg.CentralConfig) (interface{}, error) { 414 cfg = &configWithNoValidation{ 415 configValidationCalled: false, 416 CentralCfg: centralConfig, 417 AgentCfg: &agentConfig{ 418 agentValidationCalled: false, 419 bProp: rootCmd.GetProperties().BoolPropertyValue("agent.bool"), 420 dProp: rootCmd.GetProperties().DurationPropertyValue("agent.duration"), 421 iProp: rootCmd.GetProperties().IntPropertyValue("agent.int"), 422 sProp: rootCmd.GetProperties().StringPropertyValue("agent.string"), 423 ssProp: rootCmd.GetProperties().StringSlicePropertyValue("agent.stringSlice"), 424 }, 425 } 426 return cfg, nil 427 } 428 var cmdHandlerInvoked bool 429 cmdHandler := func() error { 430 cmdHandlerInvoked = true 431 return nil 432 } 433 434 os.Setenv("CENTRAL_AUTH_PRIVATEKEY", "../transaction/testdata/private_key.pem") 435 os.Setenv("CENTRAL_AUTH_PUBLICKEY", "../transaction/testdata/public_key") 436 os.Setenv("CENTRAL_AUTH_CLIENTID", "serviceaccount_1234") 437 os.Setenv("CENTRAL_AUTH_URL", s.URL) 438 os.Setenv("CENTRAL_URL", s.URL) 439 os.Setenv("CENTRAL_SINGLEURL", s.URL) 440 441 rootCmd = NewRootCmd("test_with_agent_cfg", "test_with_agent_cfg", initConfigHandler, cmdHandler, corecfg.DiscoveryAgent) 442 viper.AddConfigPath("./testdata") 443 444 rootCmd.GetProperties().AddBoolProperty("agent.bool", false, "Agent Bool Property") 445 rootCmd.GetProperties().AddDurationProperty("agent.duration", 10*time.Second, "Agent Duration Property", properties.WithLowerLimit(10*time.Second)) 446 rootCmd.GetProperties().AddIntProperty("agent.int", 0, "Agent Int Property") 447 rootCmd.GetProperties().AddStringProperty("agent.string", "", "Agent String Property") 448 rootCmd.GetProperties().AddStringSliceProperty("agent.stringSlice", nil, "Agent String Slice Property") 449 450 err := rootCmd.Execute() 451 452 // should NOT be FileNotFound error 453 assert.Nil(t, err, "An unexpected error returned") 454 if err != nil { 455 _, ok := err.(viper.ConfigFileNotFoundError) 456 assert.False(t, ok, "Incorrect error returned: %s", err.Error()) 457 } 458 459 errBuf := new(bytes.Buffer) 460 rootCmd.RootCmd().SetErr(errBuf) 461 assert.Empty(t, "", errBuf.String()) 462 assert.Equal(t, false, cfg.configValidationCalled) 463 agentCfg := cfg.AgentCfg.(*agentConfig) 464 assert.Equal(t, true, agentCfg.agentValidationCalled) 465 assert.Equal(t, true, agentCfg.bProp) 466 assert.Equal(t, 30*time.Second, agentCfg.dProp) 467 assert.Equal(t, 555, agentCfg.iProp) 468 assert.Equal(t, true, cmdHandlerInvoked) 469 470 } 471 472 func TestRootCommandLoggerStdout(t *testing.T) { 473 s := newTestServer() 474 defer s.Close() 475 476 initConfigHandler := noOpInitConfigHandler 477 cmdHandler := noOpCmdHandler 478 479 os.Setenv("CENTRAL_AUTH_PRIVATEKEY", "../transaction/testdata/private_key.pem") 480 os.Setenv("CENTRAL_AUTH_PUBLICKEY", "../transaction/testdata/public_key") 481 os.Setenv("CENTRAL_AUTH_CLIENTID", "serviceaccount_1234") 482 os.Setenv("CENTRAL_AUTH_URL", s.URL) 483 os.Setenv("CENTRAL_URL", s.URL) 484 os.Setenv("CENTRAL_SINGLEURL", s.URL) 485 486 rootCmd := NewRootCmd("test_with_non_defaults", "test_with_non_defaults", initConfigHandler, cmdHandler, corecfg.DiscoveryAgent) 487 viper.AddConfigPath("./testdata") 488 489 rescueStdout := os.Stdout 490 r, w, _ := os.Pipe() 491 os.Stdout = w 492 493 err := rootCmd.Execute() 494 495 // should NOT be FileNotFound error 496 assert.Nil(t, err, "An unexpected error was received") 497 if err != nil { 498 _, ok := err.(viper.ConfigFileNotFoundError) 499 assert.False(t, ok, "Incorrect error returned: %s", err.Error()) 500 } 501 502 w.Close() 503 504 var logData map[string]string 505 scanner := bufio.NewScanner(r) 506 507 level := "info" 508 msg := "Starting test_with_non_defaults version -, Amplify Agents SDK version " 509 510 for scanner.Scan() { 511 out := scanner.Text() 512 err := json.Unmarshal([]byte(out), &logData) 513 assert.Nil(t, err, "failed to unmarshal log data") 514 if logData["level"] == level && logData["message"] == msg { 515 break 516 } 517 } 518 519 os.Stdout = rescueStdout 520 521 assert.Equal(t, level, logData["level"]) 522 assert.Equal(t, msg, logData["message"]) 523 } 524 525 func TestRootCommandLoggerFile(t *testing.T) { 526 initConfigHandler := noOpInitConfigHandler 527 cmdHandler := noOpCmdHandler 528 529 s := newTestServer() 530 defer s.Close() 531 532 os.Setenv("CENTRAL_AUTH_PRIVATEKEY", "../transaction/testdata/private_key.pem") 533 os.Setenv("CENTRAL_AUTH_PUBLICKEY", "../transaction/testdata/public_key") 534 os.Setenv("CENTRAL_AUTH_CLIENTID", "serviceaccount_1234") 535 os.Setenv("CENTRAL_AUTH_URL", s.URL) 536 os.Setenv("CENTRAL_URL", s.URL) 537 os.Setenv("CENTRAL_SINGLEURL", s.URL) 538 539 rootCmd := NewRootCmd("test_with_non_defaults", "test_with_non_defaults", initConfigHandler, cmdHandler, corecfg.DiscoveryAgent) 540 viper.AddConfigPath("./testdata") 541 rootCmd.RootCmd().SetArgs([]string{ 542 "--logOutput", 543 "file", 544 "--logFilePath", 545 "./tmplogs", 546 "--logFileName", 547 "test_with_non_defaults.log", 548 }, 549 ) 550 // Make sure to delete file 551 os.RemoveAll("./tmplogs/test_with_non_defaults.log") 552 553 fExecute := func() { 554 rootCmd.Execute() 555 } 556 assert.NotPanics(t, fExecute) 557 558 dat, err := ioutil.ReadFile("./tmplogs/test_with_non_defaults.log") 559 assert.Nil(t, err, "failed to read file") 560 scanner := bufio.NewScanner(bytes.NewReader(dat)) 561 562 var logData map[string]string 563 level := "info" 564 msg := "Starting test_with_non_defaults version -, Amplify Agents SDK version " 565 566 for scanner.Scan() { 567 out := scanner.Text() 568 err := json.Unmarshal([]byte(out), &logData) 569 assert.Nil(t, err, "failed to unmarshal log data") 570 if logData["level"] == level && logData["message"] == msg { 571 break 572 } 573 } 574 575 assert.Equal(t, level, logData["level"]) 576 assert.Equal(t, msg, logData["message"]) 577 } 578 579 func TestRootCommandLoggerStdoutAndFile(t *testing.T) { 580 initConfigHandler := noOpInitConfigHandler 581 cmdHandler := noOpCmdHandler 582 583 s := newTestServer() 584 defer s.Close() 585 586 os.Setenv("CENTRAL_AUTH_PRIVATEKEY", "../transaction/testdata/private_key.pem") 587 os.Setenv("CENTRAL_AUTH_PUBLICKEY", "../transaction/testdata/public_key") 588 os.Setenv("CENTRAL_AUTH_CLIENTID", "serviceaccount_1234") 589 os.Setenv("CENTRAL_AUTH_URL", s.URL) 590 os.Setenv("CENTRAL_URL", s.URL) 591 os.Setenv("CENTRAL_SINGLEURL", s.URL) 592 593 rootCmd := NewRootCmd("test_with_non_defaults", "test_with_non_defaults", initConfigHandler, cmdHandler, corecfg.DiscoveryAgent) 594 viper.AddConfigPath("./testdata") 595 rootCmd.RootCmd().SetArgs([]string{ 596 "--logOutput", 597 "both", 598 "--logFilePath", 599 "./tmplogs", 600 "--logFileName", 601 "test_with_non_defaults.log", 602 }, 603 ) 604 rescueStdout := os.Stdout 605 r, w, _ := os.Pipe() 606 os.Stdout = w 607 608 fExecute := func() { 609 rootCmd.Execute() 610 } 611 // Make sure to delete file 612 os.Remove("./tmplogs/test_with_non_defaults.log") 613 assert.NotPanics(t, fExecute) 614 w.Close() 615 out, _ := ioutil.ReadAll(r) 616 os.Stdout = rescueStdout 617 var logData map[string]string 618 json.Unmarshal([]byte(out), &logData) 619 620 dat, err := ioutil.ReadFile("./tmplogs/test_with_non_defaults.log") 621 assert.Nil(t, err) 622 assert.Equal(t, out, dat) 623 } 624 625 func TestRootCmdHandlerWithSecretRefProperties(t *testing.T) { 626 secret := management.Secret{ 627 ResourceMeta: v1.ResourceMeta{Name: "agentSecret"}, 628 Spec: management.SecretSpec{ 629 Data: map[string]string{ 630 "secretKey": "secretValue", 631 "cachedSecretKey": "cachedSecretValue", 632 "keyElement1.keyElement2": "secretValue2", 633 }, 634 }, 635 } 636 637 teams := []definitions.PlatformTeam{ 638 { 639 ID: "123", 640 Name: "name", 641 Default: true, 642 }, 643 } 644 645 environmentRes := &management.Environment{ 646 ResourceMeta: v1.ResourceMeta{ 647 Metadata: v1.Metadata{ID: "123"}, 648 Name: "test", 649 Title: "test", 650 }, 651 } 652 653 s := httptest.NewServer(http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { 654 if strings.Contains(req.RequestURI, "/auth") { 655 token := "{\"access_token\":\"somevalue\",\"expires_in\": 12235677}" 656 resp.Write([]byte(token)) 657 return 658 } 659 660 if strings.Contains(req.RequestURI, "/apis/management/v1alpha1/environments/test/secrets/agentSecret") { 661 buf, _ := json.Marshal(secret) 662 resp.Write(buf) 663 return 664 } 665 666 if strings.Contains(req.RequestURI, "/apis/management/v1alpha1/environments/test") { 667 buf, _ := json.Marshal(environmentRes) 668 resp.Write(buf) 669 return 670 } 671 672 if strings.Contains(req.RequestURI, "/api/v1/platformTeams") { 673 buf, _ := json.Marshal(teams) 674 resp.Write(buf) 675 return 676 } 677 })) 678 defer s.Close() 679 680 var rootCmd AgentRootCmd 681 var cfg *configWithNoValidation 682 initConfigHandler := func(centralConfig corecfg.CentralConfig) (interface{}, error) { 683 cfg = &configWithNoValidation{ 684 configValidationCalled: false, 685 CentralCfg: centralConfig, 686 AgentCfg: &agentConfig{ 687 agentValidationCalled: false, 688 sProp: rootCmd.GetProperties().StringPropertyValue("agent.string"), 689 sPropExt: rootCmd.GetProperties().StringPropertyValue("agent.stringExt"), 690 }, 691 } 692 return cfg, nil 693 } 694 var cmdHandlerInvoked bool 695 cmdHandler := func() error { 696 cmdHandlerInvoked = true 697 return nil 698 } 699 700 os.Setenv("CENTRAL_AUTH_URL", s.URL+"/auth") 701 os.Setenv("CENTRAL_AUTH_CLIENTID", "serviceaccount_1234") 702 os.Setenv("CENTRAL_AUTH_PRIVATEKEY", "../transaction/testdata/private_key.pem") 703 os.Setenv("CENTRAL_AUTH_PUBLICKEY", "../transaction/testdata/public_key") 704 os.Setenv("CENTRAL_URL", s.URL) 705 os.Setenv("CENTRAL_SINGLEURL", s.URL) 706 os.Setenv("CENTRAL_ENVIRONMENT", "test") 707 708 rootCmd = NewRootCmd("test_with_agent_cfg", "test_with_agent_cfg", initConfigHandler, cmdHandler, corecfg.DiscoveryAgent) 709 viper.AddConfigPath("./testdata") 710 711 rootCmd.GetProperties().AddStringProperty("agent.string", "", "Agent String Property") 712 rootCmd.GetProperties().AddStringSliceProperty("agent.stringSlice", nil, "Agent String Slice Property") 713 714 // Case 1 : No secret resolution - use the value in config 715 os.Setenv("AGENT_STRING", "testValue") 716 os.Setenv("AGENT_STRINGEXT", "anotherTestValue") 717 err := rootCmd.Execute() 718 assert.Nil(t, err, "An unexpected error returned") 719 agentCfg := cfg.AgentCfg.(*agentConfig) 720 assert.Equal(t, true, agentCfg.agentValidationCalled) 721 assert.Equal(t, "testValue", agentCfg.sProp) 722 assert.Equal(t, "anotherTestValue", agentCfg.sPropExt) 723 assert.Equal(t, true, cmdHandlerInvoked) 724 725 // Case 2 : Invalid secret resolution - secret ref with invalid secret name, 726 // config value will be set to empty string 727 cfg = nil 728 agentCfg.agentValidationCalled = false 729 cmdHandlerInvoked = false 730 os.Setenv("AGENT_STRING", "@Secret.invalidSecret.secretKey") 731 os.Setenv("AGENT_STRINGEXT", "@Secret.invalidSecret.cachedSecretKey") 732 err = rootCmd.Execute() 733 assert.NotNil(t, err) 734 assert.Equal(t, "agentConfig: String prop not set", err.Error()) 735 agentCfg = cfg.AgentCfg.(*agentConfig) 736 assert.Equal(t, true, agentCfg.agentValidationCalled) 737 assert.Equal(t, "", agentCfg.sProp) 738 assert.Equal(t, "", agentCfg.sPropExt) 739 assert.Equal(t, false, cmdHandlerInvoked) 740 741 // Case 3 : Invalid secret resolution - secret ref with invalid key in secret 742 // config value will be set to empty string 743 cfg = nil 744 agentCfg.agentValidationCalled = false 745 cmdHandlerInvoked = false 746 747 os.Setenv("AGENT_STRING", "@Secret.agentSecret.invalidKey") 748 os.Setenv("AGENT_STRINGEXT", "@Secret.invalidSecret.cachedSecretKey") 749 err = rootCmd.Execute() 750 assert.NotNil(t, err) 751 assert.Equal(t, "agentConfig: String prop not set", err.Error()) 752 agentCfg = cfg.AgentCfg.(*agentConfig) 753 assert.Equal(t, true, agentCfg.agentValidationCalled) 754 assert.Equal(t, "", agentCfg.sProp) 755 assert.Equal(t, "", agentCfg.sPropExt) 756 assert.Equal(t, false, cmdHandlerInvoked) 757 758 // Case 4 : Successful secret resolution - use value in secret key 759 // config value will be set to specified key in secret 760 cfg = nil 761 agentCfg.agentValidationCalled = false 762 cmdHandlerInvoked = false 763 764 os.Setenv("AGENT_STRING", "@Secret.agentSecret.secretKey") 765 os.Setenv("AGENT_STRINGEXT", "@Secret.agentSecret.cachedSecretKey") 766 err = rootCmd.Execute() 767 assert.Nil(t, err) 768 agentCfg = cfg.AgentCfg.(*agentConfig) 769 assert.Equal(t, true, agentCfg.agentValidationCalled) 770 assert.Equal(t, "secretValue", agentCfg.sProp) 771 assert.Equal(t, "cachedSecretValue", agentCfg.sPropExt) 772 assert.Equal(t, true, cmdHandlerInvoked) 773 774 // Case 5 : Successful secret resolution with key separate with dots(.) - use value in secret key 775 // config value will be set to specified key in secret 776 cfg = nil 777 agentCfg.agentValidationCalled = false 778 cmdHandlerInvoked = false 779 780 os.Setenv("AGENT_STRING", "@Secret.agentSecret.keyElement1.keyElement2") 781 err = rootCmd.Execute() 782 assert.Nil(t, err) 783 agentCfg = cfg.AgentCfg.(*agentConfig) 784 assert.Equal(t, true, agentCfg.agentValidationCalled) 785 assert.Equal(t, "secretValue2", agentCfg.sProp) 786 assert.Equal(t, true, cmdHandlerInvoked) 787 } 788 789 func noOpInitConfigHandler(centralConfig corecfg.CentralConfig) (interface{}, error) { 790 return centralConfig, nil 791 } 792 793 func noOpCmdHandler() error { 794 return nil 795 } 796 797 func newTestServer() *httptest.Server { 798 teams := []definitions.PlatformTeam{ 799 { 800 ID: "123", 801 Name: "name", 802 Default: true, 803 }, 804 } 805 806 environmentRes := &management.Environment{ 807 ResourceMeta: v1.ResourceMeta{ 808 Metadata: v1.Metadata{ID: "123"}, 809 Name: "test", 810 Title: "test", 811 }, 812 } 813 814 secret := management.Secret{ 815 ResourceMeta: v1.ResourceMeta{Name: "agentSecret"}, 816 Spec: management.SecretSpec{ 817 Data: map[string]string{ 818 "secretKey": "secretValue", 819 "cachedSecretKey": "cachedSecretValue", 820 "keyElement1.keyElement2": "secretValue2", 821 }, 822 }, 823 } 824 825 s := httptest.NewServer(http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { 826 if strings.Contains(req.RequestURI, "/auth") { 827 token := "{\"access_token\":\"somevalue\",\"expires_in\": 12235677}" 828 resp.Write([]byte(token)) 829 return 830 } 831 832 if strings.Contains(req.RequestURI, "/apis/management/v1alpha1/environments/test/secrets/agentSecret") { 833 buf, _ := json.Marshal(secret) 834 resp.Write(buf) 835 } 836 837 if strings.Contains(req.RequestURI, "/realms/Broker/protocol/openid-connect/token") { 838 token := "{\"access_token\":\"somevalue\",\"expires_in\": 12235677}" 839 resp.Write([]byte(token)) 840 return 841 } 842 843 if strings.Contains(req.RequestURI, "/apis/management/v1alpha1/environments/test/apiservices") { 844 resp.Write([]byte("response")) 845 return 846 } 847 848 if strings.Contains(req.RequestURI, "/apis/management/v1alpha1/environments/environment") { 849 buf, _ := json.Marshal(environmentRes) 850 resp.Write(buf) 851 return 852 } 853 854 if strings.Contains(req.RequestURI, "/api/v1/platformTeams") { 855 buf, _ := json.Marshal(teams) 856 resp.Write(buf) 857 return 858 } 859 })) 860 861 return s 862 } 863 864 func TestLowerAndUpperLimitDurations(t *testing.T) { 865 testCases := []struct { 866 name string 867 durationProperty string 868 defaultDuration time.Duration 869 description string 870 lowerLimit time.Duration 871 upperLimit time.Duration 872 expectPanic bool 873 }{ 874 { 875 // valid range 876 name: "Agent Duration Property - valid range", 877 durationProperty: "agent.duration", 878 defaultDuration: 25 * time.Second, 879 description: "Agent Duration Property - valid range", 880 lowerLimit: 20 * time.Second, 881 upperLimit: 40 * time.Second, 882 }, 883 { 884 // lower limit is invalid 885 /* 886 {"level":"warning","message":"value 30s is lower than the supported lower limit (40s) for configuration agentDuration","time":"2022-07-26T14:42:54-07:00"} 887 {"level":"warning","message":"config agentDuration has been set to the the default value of 25s.","time":"2022-07-26T14:42:54-07:00"} 888 */ 889 name: "Agent Duration Property - invalid lower limit", 890 durationProperty: "agent.duration", 891 defaultDuration: 40 * time.Second, 892 description: "Agent Duration Property - invalid lower limit", 893 lowerLimit: 40 * time.Second, 894 upperLimit: 50 * time.Second, 895 }, 896 { 897 // default lower than lower limit 898 name: "Agent Duration Property - invalid upper limit", 899 durationProperty: "agent.duration", 900 defaultDuration: 5 * time.Second, 901 description: "Agent Duration Property - invalid upper limit", 902 lowerLimit: 10 * time.Second, 903 upperLimit: 20 * time.Second, 904 expectPanic: true, 905 }, 906 { 907 // upper limit is invalid 908 /* 909 {"level":"warning","message":"value 30s is higher than the supported higher limit (20s) for configuration agentDuration","time":"2022-07-26T14:42:54-07:00"} 910 {"level":"warning","message":"config agentDuration has been set to the the default value of 30s.","time":"2022-07-26T14:42:54-07:00"} 911 */ 912 name: "Agent Duration Property - invalid upper limit", 913 durationProperty: "agent.duration", 914 defaultDuration: 20 * time.Second, 915 description: "Agent Duration Property - invalid upper limit", 916 lowerLimit: 10 * time.Second, 917 upperLimit: 20 * time.Second, 918 }, 919 { 920 // default higher than upper limit 921 name: "Agent Duration Property - invalid upper limit", 922 durationProperty: "agent.duration", 923 defaultDuration: 40 * time.Second, 924 description: "Agent Duration Property - invalid upper limit", 925 lowerLimit: 10 * time.Second, 926 upperLimit: 20 * time.Second, 927 expectPanic: true, 928 }, 929 { 930 // upper lower than lower limit 931 name: "Agent Duration Property - invalid upper limit", 932 durationProperty: "agent.duration", 933 defaultDuration: 15 * time.Second, 934 description: "Agent Duration Property - invalid upper limit", 935 lowerLimit: 10 * time.Second, 936 upperLimit: 5 * time.Second, 937 expectPanic: true, 938 }, 939 } 940 941 for _, test := range testCases { 942 t.Run(test.name, func(t *testing.T) { 943 s := newTestServer() 944 defer s.Close() 945 946 var rootCmd AgentRootCmd 947 var cfg *configWithValidation 948 initConfigHandler := func(centralConfig corecfg.CentralConfig) (interface{}, error) { 949 cfg = &configWithValidation{ 950 configValidationCalled: false, 951 CentralCfg: centralConfig, 952 AgentCfg: &agentConfig{ 953 agentValidationCalled: false, 954 dProp: rootCmd.GetProperties().DurationPropertyValue("agent.duration"), 955 }, 956 } 957 return cfg, nil 958 } 959 960 os.Setenv("CENTRAL_AUTH_PRIVATEKEY", "../transaction/testdata/private_key.pem") 961 os.Setenv("CENTRAL_AUTH_PUBLICKEY", "../transaction/testdata/public_key") 962 os.Setenv("CENTRAL_AUTH_CLIENTID", "serviceaccount_1234") 963 os.Setenv("CENTRAL_AUTH_URL", s.URL) 964 os.Setenv("CENTRAL_URL", s.URL) 965 os.Setenv("CENTRAL_SINGLEURL", s.URL) 966 os.Setenv("AGENT_DURATION", "30s") 967 968 rootCmd = NewRootCmd("test_with_non_defaults", "test_with_non_defaults", initConfigHandler, nil, corecfg.DiscoveryAgent) 969 viper.AddConfigPath("./testdata") 970 fExecute := func() { 971 rootCmd.GetProperties().AddDurationProperty(test.durationProperty, test.defaultDuration, test.description, properties.WithLowerLimit(test.lowerLimit), properties.WithUpperLimit(test.upperLimit)) 972 } 973 if test.expectPanic { 974 assert.Panics(t, fExecute) 975 } else { 976 assert.NotPanics(t, fExecute) 977 _ = rootCmd.Execute() 978 } 979 }) 980 } 981 }