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

     1  package api_test
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"io/ioutil"
     7  	"net/http"
     8  
     9  	"code.cloudfoundry.org/lager"
    10  	"github.com/aws/aws-sdk-go/aws/awserr"
    11  	awssecretsmanager "github.com/aws/aws-sdk-go/service/secretsmanager"
    12  	"github.com/aws/aws-sdk-go/service/secretsmanager/secretsmanageriface"
    13  	awsssm "github.com/aws/aws-sdk-go/service/ssm"
    14  	"github.com/aws/aws-sdk-go/service/ssm/ssmiface"
    15  	"github.com/pf-qiu/concourse/v6/atc/creds/credhub"
    16  	"github.com/pf-qiu/concourse/v6/atc/creds/secretsmanager"
    17  	"github.com/pf-qiu/concourse/v6/atc/creds/ssm"
    18  	"github.com/pf-qiu/concourse/v6/atc/creds/vault"
    19  	. "github.com/pf-qiu/concourse/v6/atc/testhelpers"
    20  	vaultapi "github.com/hashicorp/vault/api"
    21  	. "github.com/onsi/ginkgo"
    22  	. "github.com/onsi/gomega"
    23  	"github.com/onsi/gomega/ghttp"
    24  )
    25  
    26  type MockSsmService struct {
    27  	ssmiface.SSMAPI
    28  
    29  	stubGetParameter func(input *awsssm.GetParameterInput) (*awsssm.GetParameterOutput, error)
    30  }
    31  
    32  func (m *MockSsmService) GetParameter(input *awsssm.GetParameterInput) (*awsssm.GetParameterOutput, error) {
    33  	return m.stubGetParameter(input)
    34  }
    35  
    36  type MockSecretsManagerService struct {
    37  	secretsmanageriface.SecretsManagerAPI
    38  
    39  	stubGetSecretValue func(input *awssecretsmanager.GetSecretValueInput) (*awssecretsmanager.GetSecretValueOutput, error)
    40  }
    41  
    42  func (m *MockSecretsManagerService) GetSecretValue(input *awssecretsmanager.GetSecretValueInput) (*awssecretsmanager.GetSecretValueOutput, error) {
    43  	return m.stubGetSecretValue(input)
    44  }
    45  
    46  var _ = Describe("Pipelines API", func() {
    47  	Describe("GET /api/v1/info", func() {
    48  		var response *http.Response
    49  
    50  		JustBeforeEach(func() {
    51  			var err error
    52  
    53  			response, err = client.Get(server.URL + "/api/v1/info")
    54  			Expect(err).NotTo(HaveOccurred())
    55  		})
    56  
    57  		It("returns Content-Type 'application/json'", func() {
    58  			expectedHeaderEntries := map[string]string{
    59  				"Content-Type": "application/json",
    60  			}
    61  			Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
    62  		})
    63  
    64  		It("contains the version", func() {
    65  			body, err := ioutil.ReadAll(response.Body)
    66  			Expect(err).NotTo(HaveOccurred())
    67  
    68  			Expect(body).To(MatchJSON(`{
    69  				"version": "1.2.3",
    70  				"worker_version": "4.5.6",
    71  				"external_url": "https://example.com",
    72  				"cluster_name": "Test Cluster"
    73  			}`))
    74  		})
    75  	})
    76  
    77  	Describe("GET /api/v1/info/creds", func() {
    78  		var (
    79  			response   *http.Response
    80  			credServer *ghttp.Server
    81  			body       []byte
    82  		)
    83  
    84  		JustBeforeEach(func() {
    85  			req, err := http.NewRequest("GET", server.URL+"/api/v1/info/creds", nil)
    86  			Expect(err).NotTo(HaveOccurred())
    87  			req.Header.Set("Content-Type", "application/json")
    88  
    89  			response, err = client.Do(req)
    90  			Expect(err).NotTo(HaveOccurred())
    91  
    92  			Expect(response.StatusCode).To(Equal(http.StatusOK))
    93  			expectedHeaderEntries := map[string]string{
    94  				"Content-Type": "application/json",
    95  			}
    96  			Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
    97  
    98  			body, err = ioutil.ReadAll(response.Body)
    99  			Expect(err).NotTo(HaveOccurred())
   100  		})
   101  
   102  		Context("SSM", func() {
   103  			var mockService MockSsmService
   104  
   105  			BeforeEach(func() {
   106  				fakeAccess.IsAuthenticatedReturns(true)
   107  				fakeAccess.IsAdminReturns(true)
   108  
   109  				ssmAccess := ssm.NewSsm(lager.NewLogger("ssm_test"), &mockService, nil)
   110  				ssmManager := &ssm.SsmManager{
   111  					AwsAccessKeyID:         "",
   112  					AwsSecretAccessKey:     "",
   113  					AwsSessionToken:        "",
   114  					AwsRegion:              "blah",
   115  					PipelineSecretTemplate: "pipeline-secret-template",
   116  					TeamSecretTemplate:     "team-secret-template",
   117  					Ssm:                    ssmAccess,
   118  				}
   119  
   120  				credsManagers["ssm"] = ssmManager
   121  			})
   122  
   123  			Context("returns configured ssm manager", func() {
   124  				Context("get ssm manager info returns error", func() {
   125  
   126  					BeforeEach(func() {
   127  						mockService.stubGetParameter = func(input *awsssm.GetParameterInput) (*awsssm.GetParameterOutput, error) {
   128  							return nil, errors.New("some error occured")
   129  						}
   130  					})
   131  
   132  					It("includes the error in json response", func() {
   133  						Expect(body).To(MatchJSON(`{
   134            "ssm": {
   135  						"aws_region": "blah",
   136  						"health": {
   137  							"error": "some error occured",
   138  							"method": "GetParameter"
   139  						},
   140  						"pipeline_secret_template": "pipeline-secret-template",
   141  						"team_secret_template": "team-secret-template"
   142            }
   143          }`))
   144  					})
   145  				})
   146  
   147  				Context("get ssm manager info", func() {
   148  
   149  					BeforeEach(func() {
   150  						mockService.stubGetParameter = func(input *awsssm.GetParameterInput) (*awsssm.GetParameterOutput, error) {
   151  							return nil, awserr.New(awsssm.ErrCodeParameterNotFound, "dontcare", nil)
   152  						}
   153  					})
   154  
   155  					It("includes the ssm health info in json response", func() {
   156  						Expect(body).To(MatchJSON(`{
   157            "ssm": {
   158  						"aws_region": "blah",
   159  						"health": {
   160  							"response": {
   161  								"status": "UP"
   162  							},
   163  							"method": "GetParameter"
   164  						},
   165  						"pipeline_secret_template": "pipeline-secret-template",
   166  						"team_secret_template": "team-secret-template"
   167            }
   168          }`))
   169  					})
   170  				})
   171  			})
   172  		})
   173  
   174  		Context("vault", func() {
   175  			BeforeEach(func() {
   176  				fakeAccess.IsAuthenticatedReturns(true)
   177  				fakeAccess.IsAdminReturns(true)
   178  
   179  				authConfig := vault.AuthConfig{
   180  					Backend:       "backend-server",
   181  					BackendMaxTTL: 20,
   182  					RetryMax:      5,
   183  					RetryInitial:  2,
   184  				}
   185  
   186  				tls := vault.TLSConfig{
   187  					CACert:     "",
   188  					ServerName: "server-name",
   189  				}
   190  
   191  				credServer = ghttp.NewServer()
   192  				vaultManager := &vault.VaultManager{
   193  					URL:             credServer.URL(),
   194  					Namespace:       "testnamespace",
   195  					PathPrefix:      "testpath",
   196  					LookupTemplates: []string{"/{{.Team}}/{{.Pipeline}}/{{.Secret}}", "/{{.Team}}/{{.Secret}}"},
   197  					TLS:             tls,
   198  					Auth:            authConfig,
   199  				}
   200  
   201  				err := vaultManager.Init(lager.NewLogger("test"))
   202  				Expect(err).ToNot(HaveOccurred())
   203  
   204  				credsManagers["vault"] = vaultManager
   205  
   206  				credServer.RouteToHandler("GET", "/v1/sys/health", ghttp.RespondWithJSONEncoded(
   207  					http.StatusOK,
   208  					&vaultapi.HealthResponse{
   209  						Initialized:                true,
   210  						Sealed:                     false,
   211  						Standby:                    false,
   212  						ReplicationPerformanceMode: "foo",
   213  						ReplicationDRMode:          "blah",
   214  						ServerTimeUTC:              0,
   215  						Version:                    "1.0.0",
   216  					},
   217  				))
   218  			})
   219  
   220  			Context("get vault health info returns error", func() {
   221  				BeforeEach(func() {
   222  					credServer.RouteToHandler("GET", "/v1/sys/health", ghttp.RespondWithJSONEncoded(
   223  						http.StatusInternalServerError,
   224  						"some error occurred",
   225  					))
   226  				})
   227  
   228  				It("returns configured creds manager with error", func() {
   229  					var errorBody struct {
   230  						Vault struct {
   231  							Health struct {
   232  								Error  string `json:"error"`
   233  								Method string `json:"method"`
   234  							} `json:"health"`
   235  						} `json:"vault"`
   236  					}
   237  
   238  					err := json.Unmarshal(body, &errorBody)
   239  					Expect(err).ToNot(HaveOccurred())
   240  
   241  					Expect(errorBody.Vault.Health.Error).To(ContainSubstring("some error occurred"))
   242  				})
   243  			})
   244  
   245  			Context("get vault health info", func() {
   246  				BeforeEach(func() {
   247  					credServer.RouteToHandler("GET", "/v1/sys/health", ghttp.RespondWithJSONEncoded(
   248  						http.StatusOK,
   249  						&vaultapi.HealthResponse{
   250  							Initialized:                true,
   251  							Sealed:                     false,
   252  							Standby:                    false,
   253  							ReplicationPerformanceMode: "foo",
   254  							ReplicationDRMode:          "blah",
   255  							ServerTimeUTC:              0,
   256  							Version:                    "1.0.0",
   257  						},
   258  					))
   259  				})
   260  
   261  				It("returns configured creds manager", func() {
   262  					Expect(body).To(MatchJSON(`{
   263            "vault": {
   264              "url": "` + credServer.URL() + `",
   265              "path_prefix": "testpath",
   266              "lookup_templates": ["/{{.Team}}/{{.Pipeline}}/{{.Secret}}", "/{{.Team}}/{{.Secret}}"],
   267  			"shared_path": "",
   268  			"namespace": "testnamespace",
   269              "ca_cert": "",
   270              "server_name": "server-name",
   271  						"auth_backend": "backend-server",
   272  						"auth_max_ttl": 20,
   273  						"auth_retry_max": 5,
   274  						"auth_retry_initial": 2,
   275  						"health": {
   276  							"response": {
   277                    "initialized": true,
   278                    "sealed": false,
   279                    "standby": false,
   280  				  "performance_standby": false,
   281                    "replication_performance_mode": "foo",
   282                    "replication_dr_mode": "blah",
   283                    "server_time_utc": 0,
   284                    "version": "1.0.0"
   285                  },
   286                  "method": "/v1/sys/health"
   287  						}
   288            }
   289          }`))
   290  				})
   291  			})
   292  		})
   293  
   294  		Context("credhub", func() {
   295  			var (
   296  				tls credhub.TLS
   297  				uaa credhub.UAA
   298  			)
   299  
   300  			BeforeEach(func() {
   301  				fakeAccess.IsAuthenticatedReturns(true)
   302  				fakeAccess.IsAdminReturns(true)
   303  
   304  				tls = credhub.TLS{
   305  					CACerts: []string{},
   306  				}
   307  				uaa = credhub.UAA{
   308  					ClientId:     "client-id",
   309  					ClientSecret: "client-secret",
   310  				}
   311  			})
   312  
   313  			Context("get credhub help info succeeds", func() {
   314  				BeforeEach(func() {
   315  					credServer = ghttp.NewServer()
   316  					credServer.RouteToHandler("GET", "/health", ghttp.RespondWithJSONEncoded(
   317  						http.StatusOK, map[string]string{
   318  							"status": "UP",
   319  						},
   320  					))
   321  
   322  					credhubManager := &credhub.CredHubManager{
   323  						URL:        credServer.URL(),
   324  						PathPrefix: "some-prefix",
   325  						TLS:        tls,
   326  						UAA:        uaa,
   327  						Client:     &credhub.LazyCredhub{},
   328  					}
   329  
   330  					credsManagers["credhub"] = credhubManager
   331  				})
   332  
   333  				It("returns configured creds manager with response", func() {
   334  					Expect(body).To(MatchJSON(`{
   335  					"credhub": {
   336  						"url": "` + credServer.URL() + `",
   337  						"ca_certs": [],
   338  						"health": {
   339  							"response": {
   340  								"status": "UP"
   341  							},
   342  							"method": "/health"
   343  						},
   344  						"path_prefix": "some-prefix",
   345  						"uaa_client_id": "client-id"
   346  						}
   347  					}`))
   348  				})
   349  			})
   350  
   351  			Context("get credhub health info returns error", func() {
   352  				type responseSkeleton struct {
   353  					CredHub struct {
   354  						Url     string   `json:"url"`
   355  						CACerts []string `json:"ca_certs"`
   356  						Health  struct {
   357  							Error    string `json:"error"`
   358  							Response struct {
   359  								Status string `json:"status"`
   360  							} `json:"response"`
   361  							Method string `json:"method"`
   362  						} `json:"health"`
   363  						PathPrefix  string `json:"path_prefix"`
   364  						UAAClientId string `json:"uaa_client_id"`
   365  					} `json:"credhub"`
   366  				}
   367  
   368  				BeforeEach(func() {
   369  					credhubManager := &credhub.CredHubManager{
   370  						URL:        "http://wrong.inexistent.tld",
   371  						PathPrefix: "some-prefix",
   372  						TLS:        tls,
   373  						UAA:        uaa,
   374  						Client:     &credhub.LazyCredhub{},
   375  					}
   376  
   377  					credsManagers["credhub"] = credhubManager
   378  				})
   379  
   380  				It("returns configured creds manager with error", func() {
   381  					var parsedResponse responseSkeleton
   382  
   383  					err := json.Unmarshal(body, &parsedResponse)
   384  					Expect(err).ToNot(HaveOccurred())
   385  
   386  					Expect(parsedResponse.CredHub.Url).To(Equal("http://wrong.inexistent.tld"))
   387  					Expect(parsedResponse.CredHub.CACerts).To(BeEmpty())
   388  					Expect(parsedResponse.CredHub.PathPrefix).To(Equal("some-prefix"))
   389  					Expect(parsedResponse.CredHub.UAAClientId).To(Equal("client-id"))
   390  					Expect(parsedResponse.CredHub.Health.Response).ToNot(BeNil())
   391  					Expect(parsedResponse.CredHub.Health.Response.Status).To(BeEmpty())
   392  					Expect(parsedResponse.CredHub.Health.Method).To(Equal("/health"))
   393  					Expect(parsedResponse.CredHub.Health.Error).To(ContainSubstring("no such host"))
   394  				})
   395  			})
   396  		})
   397  
   398  		Context("SecretsManager", func() {
   399  			var mockService MockSecretsManagerService
   400  
   401  			BeforeEach(func() {
   402  				fakeAccess.IsAuthenticatedReturns(true)
   403  				fakeAccess.IsAdminReturns(true)
   404  
   405  				secretsManagerAccess := secretsmanager.NewSecretsManager(lager.NewLogger("ssm_test"), &mockService, nil)
   406  
   407  				secretsManager := &secretsmanager.Manager{
   408  					AwsAccessKeyID:         "",
   409  					AwsSecretAccessKey:     "",
   410  					AwsSessionToken:        "",
   411  					AwsRegion:              "blah",
   412  					PipelineSecretTemplate: "pipeline-secret-template",
   413  					TeamSecretTemplate:     "team-secret-template",
   414  					SecretManager:          secretsManagerAccess,
   415  				}
   416  
   417  				credsManagers["secretsmanager"] = secretsManager
   418  			})
   419  
   420  			Context("returns configured secretsmanager manager", func() {
   421  				Context("get secretsmanager info returns error", func() {
   422  
   423  					BeforeEach(func() {
   424  						mockService.stubGetSecretValue = func(input *awssecretsmanager.GetSecretValueInput) (*awssecretsmanager.GetSecretValueOutput, error) {
   425  							return nil, errors.New("some error occurred")
   426  						}
   427  					})
   428  
   429  					It("includes the error in json response", func() {
   430  						Expect(body).To(MatchJSON(`{
   431  					"secretsmanager": {
   432  						"aws_region": "blah",
   433  						"pipeline_secret_template": "pipeline-secret-template",
   434  						"team_secret_template": "team-secret-template",
   435  						"health": {
   436  							"error": "some error occurred",
   437  							"method": "GetSecretValue"
   438  						}
   439  					}
   440  				}`))
   441  					})
   442  
   443  				})
   444  
   445  				Context("get secretsmanager info", func() {
   446  
   447  					BeforeEach(func() {
   448  						mockService.stubGetSecretValue = func(input *awssecretsmanager.GetSecretValueInput) (*awssecretsmanager.GetSecretValueOutput, error) {
   449  
   450  							return nil, awserr.New(awssecretsmanager.ErrCodeResourceNotFoundException, "dontcare", nil)
   451  						}
   452  					})
   453  
   454  					It("include sthe secretsmanager info in json response", func() {
   455  						Expect(body).To(MatchJSON(`{
   456  					"secretsmanager": {
   457  						"aws_region": "blah",
   458  						"pipeline_secret_template": "pipeline-secret-template",
   459  						"team_secret_template": "team-secret-template",
   460  						"health": {
   461  							"response": {
   462  								"status": "UP"
   463  							},
   464  							"method": "GetSecretValue"
   465  						}
   466  					}
   467  				}`))
   468  					})
   469  				})
   470  			})
   471  
   472  		})
   473  	})
   474  })