// Copyright 2016-2017, Pulumi Corporation. All rights reserved. package test import ( "fmt" "io/ioutil" "net/http" "os" "path" "strings" "testing" "time" "github.com/pulumi/pulumi/pkg/testing/integration" "github.com/stretchr/testify/assert" ) func TestExamples(t *testing.T) { awsRegion := os.Getenv("AWS_REGION") if awsRegion == "" { awsRegion = "us-west-1" fmt.Println("Defaulting AWS_REGION to 'us-west-1'. You can override using the AWS_REGION environment variable") } azureEnviron := os.Getenv("ARM_ENVIRONMENT") if azureEnviron == "" { azureEnviron = "public" fmt.Println("Defaulting ARM_ENVIRONMENT to 'public'. You can override using the ARM_ENVIRONMENT variable") } cwd, err := os.Getwd() if !assert.NoError(t, err, "expected a valid working directory: %v", err) { return } base := integration.ProgramTestOptions{ Tracing: "https://tracing.pulumi-engineering.com/collector/api/v1/spans", } examples := []integration.ProgramTestOptions{ base.With(integration.ProgramTestOptions{ Dir: path.Join(cwd, "..", "..", "aws-js-s3-folder"), SkipBuild: true, Config: map[string]string{ "aws:region": awsRegion, }, ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) { assertHTTPResult(t, "http://"+stack.Outputs["websiteUrl"].(string), func(body string) bool { return assert.Contains(t, body, "Hello, Pulumi!") }) }, }), base.With(integration.ProgramTestOptions{ Dir: path.Join(cwd, "..", "..", "aws-js-s3-folder-component"), SkipBuild: true, Config: map[string]string{ "aws:region": awsRegion, }, ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) { assertHTTPResult(t, stack.Outputs["websiteUrl"].(string), func(body string) bool { return assert.Contains(t, body, "Hello, Pulumi!") }) }, }), base.With(integration.ProgramTestOptions{ Dir: path.Join(cwd, "..", "..", "aws-js-webserver"), SkipBuild: true, Config: map[string]string{ "aws:region": awsRegion, }, ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) { assertHTTPHelloWorld(t, stack.Outputs["publicHostName"]) }, }), base.With(integration.ProgramTestOptions{ Dir: path.Join(cwd, "..", "..", "aws-js-webserver-component"), SkipBuild: true, Config: map[string]string{ "aws:region": awsRegion, }, ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) { assertHTTPHelloWorld(t, stack.Outputs["webUrl"]) }, }), base.With(integration.ProgramTestOptions{ Dir: path.Join(cwd, "..", "..", "azure-js-webserver"), SkipBuild: true, Config: map[string]string{ "azure:environment": azureEnviron, "username": "testuser", "password": "testTEST1234+-*/", }, ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) { assertHTTPHelloWorld(t, stack.Outputs["publicIP"]) }, }), base.With(integration.ProgramTestOptions{ Dir: path.Join(cwd, "..", "..", "azure-ts-functions"), SkipBuild: true, Config: map[string]string{ "azure:environment": azureEnviron, }, ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) { assertHTTPResult(t, stack.Outputs["endpoint"], func(body string) bool { return assert.Contains(t, body, "Greetings from Azure Functions!") }) }, }), base.With(integration.ProgramTestOptions{ Dir: path.Join(cwd, "..", "..", "azure-ts-appservice"), SkipBuild: true, Config: map[string]string{ "azure:environment": azureEnviron, "sqlPassword": "2@Password@2", }, ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) { assertHTTPResult(t, stack.Outputs["endpoint"], func(body string) bool { return assert.Contains(t, body, "Greetings from Azure App Service!") }) }, }), base.With(integration.ProgramTestOptions{ Dir: path.Join(cwd, "..", "..", "aws-ts-ruby-on-rails"), SkipBuild: true, Config: map[string]string{ "aws:region": awsRegion, "dbUser": "testUser", "dbPassword": "2@Password@2", "dbRootPassword": "2@Password@2", }, ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) { // Due to setup time on the vm this output does not show up for several minutes so // increase wait time a bit maxWait := 10 * time.Minute assertHTTPResultWithRetry(t, stack.Outputs["websiteURL"], maxWait, func(body string) bool { return assert.Contains(t, body, "New Note") }) }, }), base.With(integration.ProgramTestOptions{ Dir: path.Join(cwd, "..", "..", "cloud-js-api"), SkipBuild: true, Config: map[string]string{ "aws:region": awsRegion, }, ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) { assertHTTPResult(t, stack.Outputs["endpoint"].(string)+"/hello", func(body string) bool { return assert.Contains(t, body, "{\"route\":\"hello\",\"count\":1}") }) }, }), base.With(integration.ProgramTestOptions{ Dir: path.Join(cwd, "..", "..", "cloud-js-containers"), SkipBuild: true, Config: map[string]string{ // use us-west-2 to assure fargate "aws:region": "us-west-2", "cloud-aws:useFargate": "true", }, ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) { assertHTTPResult(t, stack.Outputs["hostname"], func(body string) bool { return assert.Contains(t, body, "

Hello, containers!

") }) }, }), base.With(integration.ProgramTestOptions{ Dir: path.Join(cwd, "..", "..", "cloud-js-httpserver"), SkipBuild: true, Config: map[string]string{ "cloud:provider": "aws", "aws:region": awsRegion, }, ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) { assertHTTPResult(t, stack.Outputs["endpoint"].(string)+"/hello", func(body string) bool { return assert.Contains(t, body, "{\"route\":\"/hello\",\"count\":1}") }) }, }), base.With(integration.ProgramTestOptions{ Dir: path.Join(cwd, "..", "..", "cloud-ts-url-shortener-cache-http"), SkipBuild: true, Config: map[string]string{ // use us-west-2 to assure fargate "aws:region": "us-west-2", "url-shortener-cache:redisPassword": "s3cr7Password", "cloud:provider": "aws", "cloud-aws:useFargate": "true", }, // TODO: This test is not returning a valid payload see issue: https://github.com/pulumi/examples/issues/155 // ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) { // assertHTTPResult(t, stack.Outputs["endpointUrl"], func(body string) bool { // return assert.Contains(t, body, "Short URL Manager") // }) // }, }), // TODO: This test fails due to a bug in the Terraform Azure provider in which the // service principal is not available when attempting to create the K8s cluster. // See the azure-ts-aks-example readme for more detail. // base.With(integration.ProgramTestOptions{ // Dir: path.Join(cwd, "..", "..", "azure-ts-aks-mean"), // SkipBuild: true, // Config: map[string]string{ // "azure:environment": azureEnviron, // "password": "testTEST1234+_^$", // "sshPublicKey": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDeREOgHTUgPT00PTr7iQF9JwZQ4QF1VeaLk2nHKRvWYOCiky6hDtzhmLM0k0Ib9Y7cwFbhObR+8yZpCgfSX3Hc3w2I1n6lXFpMfzr+wdbpx97N4fc1EHGUr9qT3UM1COqN6e/BEosQcMVaXSCpjqL1jeNaRDAnAS2Y3q1MFeXAvj9rwq8EHTqqAc1hW9Lq4SjSiA98STil5dGw6DWRhNtf6zs4UBy8UipKsmuXtclR0gKnoEP83ahMJOpCIjuknPZhb+HsiNjFWf+Os9U6kaS5vGrbXC8nggrVE57ow88pLCBL+3mBk1vBg6bJuLBCp2WTqRzDMhSDQ3AcWqkucGqf dremy@remthinkpad", // }, // ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) { // assertHTTPResult(t, stack.Outputs["endpoint"], func(body string) bool { // return assert.Contains(t, body, "Node/Angular Todo App>") // }) // }, // }), // TODO[pulumi/pulumi#1606] This test is failing in CI, disabling until this issue is resolved. // base.With(integration.ProgramTestOptions{ // Dir: path.Join(cwd, "..", "..", "aws-py-webserver"), // Verbose: true, // DebugLogLevel: 8, // DebugUpdates: true, // SkipBuild: true, // Config: map[string]string{ // "aws:region": awsRegion, // }, // ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) { // assertHTTPHelloWorld(t, stack.Outputs["public_dns"]) // }, // }), } longExamples := []integration.ProgramTestOptions{ base.With(integration.ProgramTestOptions{ Dir: path.Join(cwd, "..", "..", "azure-ts-aks-helm"), SkipBuild: true, Config: map[string]string{ "azure:environment": azureEnviron, "password": "testTEST1234+_^$", "sshPublicKey": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDeREOgHTUgPT00PTr7iQF9JwZQ4QF1VeaLk2nHKRvWYOCiky6hDtzhmLM0k0Ib9Y7cwFbhObR+8yZpCgfSX3Hc3w2I1n6lXFpMfzr+wdbpx97N4fc1EHGUr9qT3UM1COqN6e/BEosQcMVaXSCpjqL1jeNaRDAnAS2Y3q1MFeXAvj9rwq8EHTqqAc1hW9Lq4SjSiA98STil5dGw6DWRhNtf6zs4UBy8UipKsmuXtclR0gKnoEP83ahMJOpCIjuknPZhb+HsiNjFWf+Os9U6kaS5vGrbXC8nggrVE57ow88pLCBL+3mBk1vBg6bJuLBCp2WTqRzDMhSDQ3AcWqkucGqf dremy@remthinkpad", }, ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) { assertHTTPResult(t, stack.Outputs["serviceIP"], func(body string) bool { return assert.Contains(t, body, "It works!") }) }, }), } // Only include the long examples on non-Short test runs if !testing.Short() { fmt.Println("Running full test suite including longer tests. To run only shorter tests run with the -short option.") examples = append(examples, longExamples...) } else { fmt.Println("Running short test suite. To run full test suite run without -short option") } for _, ex := range examples { example := ex t.Run(example.Dir, func(t *testing.T) { integration.ProgramTest(t, &example) }) } } func assertHTTPResult(t *testing.T, output interface{}, check func(string) bool) bool { return assertHTTPResultWithRetry(t, output, 3*time.Minute, check) } func assertHTTPResultWithRetry(t *testing.T, output interface{}, maxWait time.Duration, check func(string) bool) bool { hostname, ok := output.(string) if !assert.True(t, ok, fmt.Sprintf("expected `%s` output", output)) { return false } if !(strings.HasPrefix(hostname, "http://") || strings.HasPrefix(hostname, "https://")) { hostname = fmt.Sprintf("http://%s", hostname) } var err error var resp *http.Response startTime := time.Now() count, sleep := 0, 0 for true { now := time.Now() resp, err = http.Get(hostname) if err == nil { break } if now.Sub(startTime) >= maxWait { fmt.Printf("Timeout after %v. Unable to http.get %v successfully.", maxWait, hostname) break } count++ // delay 10s, 20s, then 30s and stay at 30s if sleep > 30 { sleep = 30 } else { sleep += 10 } time.Sleep(time.Duration(sleep) * time.Second) fmt.Printf("Http Error: %v\n", err) fmt.Printf(" Retry: %v, elapsed wait: %v, max wait %v\n", count, now.Sub(startTime), maxWait) } if !assert.NoError(t, err) { return false } // Read the body defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if !assert.NoError(t, err) { return false } // Verify it matches expectations return check(string(body)) } func assertHTTPHelloWorld(t *testing.T, output interface{}) bool { return assertHTTPResult(t, output, func(s string) bool { return assert.Equal(t, "Hello, World!\n", s) }) }