1package ssm_test 2 3import ( 4 "errors" 5 "strconv" 6 7 "code.cloudfoundry.org/lager/lagertest" 8 9 "github.com/concourse/concourse/atc/creds" 10 11 "github.com/aws/aws-sdk-go/aws" 12 "github.com/aws/aws-sdk-go/aws/awserr" 13 "github.com/aws/aws-sdk-go/service/ssm" 14 "github.com/aws/aws-sdk-go/service/ssm/ssmiface" 15 . "github.com/concourse/concourse/atc/creds/ssm" 16 "github.com/concourse/concourse/vars" 17 18 . "github.com/onsi/ginkgo" 19 . "github.com/onsi/gomega" 20 . "github.com/onsi/gomega/gstruct" 21) 22 23type mockPathResultPage struct { 24 params map[string]string 25 err error 26} 27 28func (page mockPathResultPage) ToGetParametersByPathOutput() (*ssm.GetParametersByPathOutput, error) { 29 if page.err != nil { 30 return nil, page.err 31 } 32 params := make([]*ssm.Parameter, 0, len(page.params)) 33 for name, value := range page.params { 34 params = append(params, &ssm.Parameter{ 35 Name: aws.String(name), 36 Value: aws.String(value), 37 }) 38 } 39 return &ssm.GetParametersByPathOutput{Parameters: params}, nil 40} 41 42type MockSsmService struct { 43 ssmiface.SSMAPI 44 45 stubGetParameter func(name string) (string, error) 46 stubGetParametersByPathPages func(path string) []mockPathResultPage 47} 48 49func (mock *MockSsmService) GetParameter(input *ssm.GetParameterInput) (*ssm.GetParameterOutput, error) { 50 if mock.stubGetParameter == nil { 51 return nil, errors.New("stubGetParameter is not defined") 52 } 53 Expect(input).ToNot(BeNil()) 54 Expect(input.Name).ToNot(BeNil()) 55 Expect(input.WithDecryption).To(PointTo(Equal(true))) 56 value, err := mock.stubGetParameter(*input.Name) 57 if err != nil { 58 return nil, err 59 } 60 return &ssm.GetParameterOutput{Parameter: &ssm.Parameter{Value: &value}}, nil 61} 62 63func (mock *MockSsmService) GetParametersByPathPages(input *ssm.GetParametersByPathInput, fn func(*ssm.GetParametersByPathOutput, bool) bool) error { 64 if mock.stubGetParametersByPathPages == nil { 65 return errors.New("stubGetParametersByPathPages is not defined") 66 } 67 Expect(input).NotTo(BeNil()) 68 Expect(input.Path).NotTo(BeNil()) 69 Expect(input.Recursive).To(PointTo(Equal(true))) 70 Expect(input.WithDecryption).To(PointTo(Equal(true))) 71 Expect(input.MaxResults).To(PointTo(BeEquivalentTo(10))) 72 allPages := mock.stubGetParametersByPathPages(*input.Path) 73 for n, page := range allPages { 74 params, err := page.ToGetParametersByPathOutput() 75 if err != nil { 76 return err 77 } 78 params.NextToken = aws.String(strconv.Itoa(n + 1)) 79 lastPage := (n == len(allPages)-1) 80 if !fn(params, lastPage) { 81 break 82 } 83 } 84 return nil 85} 86 87var _ = Describe("Ssm", func() { 88 var ssmAccess *Ssm 89 var variables vars.Variables 90 var varDef vars.VariableDefinition 91 var mockService MockSsmService 92 93 JustBeforeEach(func() { 94 varDef = vars.VariableDefinition{Ref: vars.VariableReference{Path: "cheery"}} 95 t1, err := creds.BuildSecretTemplate("t1", DefaultPipelineSecretTemplate) 96 Expect(t1).NotTo(BeNil()) 97 Expect(err).To(BeNil()) 98 t2, err := creds.BuildSecretTemplate("t2", DefaultTeamSecretTemplate) 99 Expect(t2).NotTo(BeNil()) 100 Expect(err).To(BeNil()) 101 ssmAccess = NewSsm(lagertest.NewTestLogger("ssm_test"), &mockService, []*creds.SecretTemplate{t1, t2}) 102 variables = creds.NewVariables(ssmAccess, "alpha", "bogus", false) 103 Expect(ssmAccess).NotTo(BeNil()) 104 mockService.stubGetParameter = func(input string) (string, error) { 105 if input == "/concourse/alpha/bogus/cheery" { 106 return "ssm decrypted value", nil 107 } 108 return "", awserr.New(ssm.ErrCodeParameterNotFound, "", nil) 109 } 110 mockService.stubGetParametersByPathPages = func(path string) []mockPathResultPage { 111 return []mockPathResultPage{} 112 } 113 }) 114 115 Describe("Get()", func() { 116 It("should get parameter if exists", func() { 117 value, found, err := variables.Get(varDef) 118 Expect(value).To(BeEquivalentTo("ssm decrypted value")) 119 Expect(found).To(BeTrue()) 120 Expect(err).To(BeNil()) 121 }) 122 123 It("should get complex paramter", func() { 124 mockService.stubGetParametersByPathPages = func(path string) []mockPathResultPage { 125 return []mockPathResultPage{ 126 { 127 params: map[string]string{ 128 "/concourse/alpha/bogus/user/name": "yours", 129 "/concourse/alpha/bogus/user/pass": "truely", 130 }, 131 err: nil, 132 }, 133 } 134 } 135 value, found, err := variables.Get(vars.VariableDefinition{Ref: vars.VariableReference{Path: "user"}}) 136 Expect(value).To(BeEquivalentTo(map[string]interface{}{ 137 "name": "yours", 138 "pass": "truely", 139 })) 140 Expect(found).To(BeTrue()) 141 Expect(err).To(BeNil()) 142 }) 143 144 It("should return numbers as strings", func() { 145 mockService.stubGetParameter = func(input string) (string, error) { 146 return "101", nil 147 } 148 value, found, err := variables.Get(varDef) 149 Expect(value).To(BeEquivalentTo("101")) 150 Expect(found).To(BeTrue()) 151 Expect(err).To(BeNil()) 152 }) 153 154 It("should get team parameter if exists", func() { 155 mockService.stubGetParameter = func(input string) (string, error) { 156 if input != "/concourse/alpha/cheery" { 157 return "", awserr.New(ssm.ErrCodeParameterNotFound, "", nil) 158 } 159 return "team decrypted value", nil 160 } 161 value, found, err := variables.Get(varDef) 162 Expect(value).To(BeEquivalentTo("team decrypted value")) 163 Expect(found).To(BeTrue()) 164 Expect(err).To(BeNil()) 165 }) 166 167 It("should return not found on error", func() { 168 mockService.stubGetParameter = nil 169 value, found, err := variables.Get(varDef) 170 Expect(value).To(BeNil()) 171 Expect(found).To(BeFalse()) 172 Expect(err).NotTo(BeNil()) 173 }) 174 175 It("should allow empty pipeline name", func() { 176 variables := creds.NewVariables(ssmAccess, "alpha", "", false) 177 mockService.stubGetParameter = func(input string) (string, error) { 178 Expect(input).To(Equal("/concourse/alpha/cheery")) 179 return "team power", nil 180 } 181 value, found, err := variables.Get(varDef) 182 Expect(value).To(BeEquivalentTo("team power")) 183 Expect(found).To(BeTrue()) 184 Expect(err).To(BeNil()) 185 }) 186 }) 187}) 188