diff --git a/README.md b/README.md index a4748403..4ad64e16 100644 --- a/README.md +++ b/README.md @@ -648,7 +648,7 @@ aws_secretsmanager: path: /prod/billing-svc/vars/mg ``` -## AWS Paramstore +## AWS Parameter store ### Authentication @@ -657,8 +657,8 @@ Your standard `AWS_DEFAULT_REGION`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` ### Features - Sync - `no` -- Mapping - `yes` -- Modes - `read`, [write: accepting PR](https://github.com/spectralops/teller) +- Mapping - `no` +- Modes - `read+write+delete` - Key format - `env` - path based - `decrypt` - available in this provider, will use KMS automatically diff --git a/pkg/providers/aws_ssm.go b/pkg/providers/aws_ssm.go index 06c8fae9..fa1d3cf1 100644 --- a/pkg/providers/aws_ssm.go +++ b/pkg/providers/aws_ssm.go @@ -8,12 +8,15 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/ssm" + "github.com/aws/aws-sdk-go-v2/service/ssm/types" "github.com/spectralops/teller/pkg/core" "github.com/spectralops/teller/pkg/logging" ) type AWSSSMClient interface { GetParameter(ctx context.Context, params *ssm.GetParameterInput, optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error) + PutParameter(ctx context.Context, params *ssm.PutParameterInput, optFns ...func(*ssm.Options)) (*ssm.PutParameterOutput, error) + DeleteParameter(ctx context.Context, params *ssm.DeleteParameterInput, optFns ...func(*ssm.Options)) (*ssm.DeleteParameterOutput, error) } type AWSSSM struct { client AWSSSMClient @@ -22,7 +25,7 @@ type AWSSSM struct { const awsssmName = "aws_ssm" -//nolint +// nolint func init() { metaInfo := core.MetaInfo{ Description: "AWS SSM (aka paramstore)", @@ -36,7 +39,7 @@ func init() { path: /prod/foobar decrypt: true `, - Ops: core.OpMatrix{Get: true}, + Ops: core.OpMatrix{Get: true, Put: true, Delete: true}, } RegisterProvider(metaInfo, NewAWSSSM) } @@ -66,11 +69,30 @@ func NewAWSSSM(logger logging.Logger) (core.Provider, error) { return &AWSSSM{client: client, logger: logger}, nil } -func (a *AWSSSM) Put(p core.KeyPath, val string) error { - return fmt.Errorf("provider %q does not implement write yet", awsssmName) +func (a *AWSSSM) Put(kp core.KeyPath, val string) error { + _, err := a.client.PutParameter(context.TODO(), &ssm.PutParameterInput{ + Name: &kp.Path, + Value: &val, + Overwrite: true, + Type: types.ParameterTypeString, + }) + if err != nil { + return err + } + + return nil } -func (a *AWSSSM) PutMapping(p core.KeyPath, m map[string]string) error { - return fmt.Errorf("provider %q does not implement write yet", awsssmName) + +func (a *AWSSSM) PutMapping(kp core.KeyPath, m map[string]string) error { + for k, v := range m { + ap := kp.SwitchPath(k) + err := a.Put(ap, v) + if err != nil { + return err + } + } + + return nil } func (a *AWSSSM) GetMapping(kp core.KeyPath) ([]core.EnvEntry, error) { @@ -78,11 +100,12 @@ func (a *AWSSSM) GetMapping(kp core.KeyPath) ([]core.EnvEntry, error) { } func (a *AWSSSM) Delete(kp core.KeyPath) error { - return fmt.Errorf("%s does not implement delete yet", awsssmName) + _, err := a.client.DeleteParameter(context.TODO(), &ssm.DeleteParameterInput{Name: &kp.Path}) + return err } func (a *AWSSSM) DeleteMapping(kp core.KeyPath) error { - return fmt.Errorf("%s does not implement delete yet", awsssmName) + return fmt.Errorf("does not support full env sync (path: %s)", kp.Path) } func (a *AWSSSM) Get(p core.KeyPath) (*core.EnvEntry, error) { diff --git a/pkg/providers/mock_providers/aws_ssm_mock.go b/pkg/providers/mock_providers/aws_ssm_mock.go index 540867c6..9e7d17d7 100644 --- a/pkg/providers/mock_providers/aws_ssm_mock.go +++ b/pkg/providers/mock_providers/aws_ssm_mock.go @@ -35,6 +35,26 @@ func (m *MockAWSSSMClient) EXPECT() *MockAWSSSMClientMockRecorder { return m.recorder } +// DeleteParameter mocks base method. +func (m *MockAWSSSMClient) DeleteParameter(ctx context.Context, params *ssm.DeleteParameterInput, optFns ...func(*ssm.Options)) (*ssm.DeleteParameterOutput, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, params} + for _, a := range optFns { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteParameter", varargs...) + ret0, _ := ret[0].(*ssm.DeleteParameterOutput) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteParameter indicates an expected call of DeleteParameter. +func (mr *MockAWSSSMClientMockRecorder) DeleteParameter(ctx, params interface{}, optFns ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, params}, optFns...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteParameter", reflect.TypeOf((*MockAWSSSMClient)(nil).DeleteParameter), varargs...) +} + // GetParameter mocks base method. func (m *MockAWSSSMClient) GetParameter(ctx context.Context, params *ssm.GetParameterInput, optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error) { m.ctrl.T.Helper() @@ -54,3 +74,23 @@ func (mr *MockAWSSSMClientMockRecorder) GetParameter(ctx, params interface{}, op varargs := append([]interface{}{ctx, params}, optFns...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetParameter", reflect.TypeOf((*MockAWSSSMClient)(nil).GetParameter), varargs...) } + +// PutParameter mocks base method. +func (m *MockAWSSSMClient) PutParameter(ctx context.Context, params *ssm.PutParameterInput, optFns ...func(*ssm.Options)) (*ssm.PutParameterOutput, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, params} + for _, a := range optFns { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "PutParameter", varargs...) + ret0, _ := ret[0].(*ssm.PutParameterOutput) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// PutParameter indicates an expected call of PutParameter. +func (mr *MockAWSSSMClientMockRecorder) PutParameter(ctx, params interface{}, optFns ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, params}, optFns...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutParameter", reflect.TypeOf((*MockAWSSSMClient)(nil).PutParameter), varargs...) +}