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