Skip to content

Commit

Permalink
refactor(create): add credential check for osdCcsAdmin when cluster s…
Browse files Browse the repository at this point in the history
…tarts to be created
  • Loading branch information
boranx committed Sep 7, 2020
1 parent d31ec75 commit ba97dc0
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 2 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.idea/
.vscode/
docs/*.1
docs/*.rst
/moactl
Expand Down
19 changes: 19 additions & 0 deletions cmd/create/cluster/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,25 @@ func run(cmd *cobra.Command, _ []string) {
reporter.Errorf("Error getting region: %v", err)
os.Exit(1)
}

// Create the AWS client:
client, err := aws.NewClient().
Logger(logger).
Region(aws.DefaultRegion).
Build()
if err != nil {
reporter.Errorf("Error creating AWS client: %v", err)
os.Exit(1)
}

// Validate AWS credentials for current user
reporter.Infof("Validating AWS credentials for CFUser...")
if err = client.ValidateCFUserCredentials(); err != nil {
reporter.Errorf("Error validating AWS credentials: %v", err)
os.Exit(1)
}
reporter.Infof("AWS credentials are valid!")

regionList, regionAZ, err := getRegionList(ocmClient, multiAZ)
if err != nil {
reporter.Errorf(fmt.Sprintf("%s", err))
Expand Down
40 changes: 40 additions & 0 deletions pkg/aws/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ const (
type Client interface {
GetRegion() string
ValidateCredentials() (bool, error)
ValidateCFUserCredentials() error
EnsureOsdCcsAdminUser(stackName string) (bool, error)
DeleteOsdCcsAdminUser(stackName string) error
GetAccessKeyFromStack(stackName string) (*AccessKey, error)
Expand Down Expand Up @@ -240,6 +241,45 @@ func (c *awsClient) ValidateCredentials() (bool, error) {
return true, nil
}

// ValidateCFUserCredentials checks if CF-IAM credentials are valid.
// it gets stack's key and actual key and compares them
// to get the stack credentials:
// aws cloudformation describe-stack-resource \
// --logical-resource-id osdCcsAdminAccessKeys --stack-name osdCcsAdminIAMUser
func (c *awsClient) ValidateCFUserCredentials() error {
name := AdminUserName
accessKeyInput := &iam.ListAccessKeysInput{
UserName: &name,
}
accessKeyList, err := c.iamClient.ListAccessKeys(accessKeyInput)
if err != nil {
return err
}

OsdCcsAdminStackNamePtr := OsdCcsAdminStackName
LogicalResourceIDPtr := "osdCcsAdminAccessKeys"
stackResourceInput := &cloudformation.DescribeStackResourceInput{
StackName: &OsdCcsAdminStackNamePtr,
LogicalResourceId: &LogicalResourceIDPtr,
}
resources, err := c.cfClient.DescribeStackResource(stackResourceInput)
if err != nil {
return err
}
cfAccessKey := resources.StackResourceDetail.PhysicalResourceId

for _, key := range accessKeyList.AccessKeyMetadata {
if *key.AccessKeyId == *cfAccessKey && *key.Status == "Active" {
return nil
}
}

return fmt.Errorf(
"Invalid CloudFormation stack credentials: %s is not valid \n"+
"you can recreate the CloudFormation stack with: \n"+
"moactl init --delete-stack && moactl init \n", name)
}

// Ensure osdCcsAdmin IAM user is created
func (c *awsClient) EnsureOsdCcsAdminUser(stackName string) (bool, error) {
// Check already existing cloudformation stack status
Expand Down
60 changes: 58 additions & 2 deletions pkg/aws/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package aws_test
import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/cloudformation"
"github.com/aws/aws-sdk-go/service/iam"
"github.com/golang/mock/gomock"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
Expand All @@ -17,15 +18,17 @@ var _ = Describe("Client", func() {
client aws.Client
mockCtrl *gomock.Controller

mockCfAPI *mocks.MockCloudFormationAPI
mockCfAPI *mocks.MockCloudFormationAPI
mockIamAPI *mocks.MockIAMAPI
)

BeforeEach(func() {
mockCtrl = gomock.NewController(GinkgoT())
mockCfAPI = mocks.NewMockCloudFormationAPI(mockCtrl)
mockIamAPI = mocks.NewMockIAMAPI(mockCtrl)
client = aws.New(
logrus.New(),
mocks.NewMockIAMAPI(mockCtrl),
mockIamAPI,
mocks.NewMockOrganizationsAPI(mockCtrl),
mocks.NewMockSTSAPI(mockCtrl),
mockCfAPI,
Expand All @@ -38,6 +41,59 @@ var _ = Describe("Client", func() {
mockCtrl.Finish()
})

Context("ValidateCFUserCredentials", func() {
var (
CFsecretID = "sut"
IAMAccessKey = "sut"
oddIAMAccessKey = "longtestkey"
status = "Active"
)
Context("when creds are OK and matches", func() {
BeforeEach(func() {
mockCfAPI.EXPECT().DescribeStackResource(gomock.Any()).Return(&cloudformation.DescribeStackResourceOutput{
StackResourceDetail: &cloudformation.StackResourceDetail{
PhysicalResourceId: &CFsecretID,
},
}, nil)

mockIamAPI.EXPECT().ListAccessKeys(gomock.Any()).Return(&iam.ListAccessKeysOutput{
AccessKeyMetadata: []*iam.AccessKeyMetadata{
{
AccessKeyId: &IAMAccessKey,
Status: &status,
},
},
}, nil)
})
It("should finish successfully and return nil", func() {
err := client.ValidateCFUserCredentials()
Expect(err).To(BeNil())
})
})

Context("when the credentials don't match", func() {
BeforeEach(func() {
mockCfAPI.EXPECT().DescribeStackResource(gomock.Any()).Return(&cloudformation.DescribeStackResourceOutput{
StackResourceDetail: &cloudformation.StackResourceDetail{
PhysicalResourceId: &CFsecretID,
},
}, nil)

mockIamAPI.EXPECT().ListAccessKeys(gomock.Any()).Return(&iam.ListAccessKeysOutput{
AccessKeyMetadata: []*iam.AccessKeyMetadata{
{
AccessKeyId: &oddIAMAccessKey,
},
},
}, nil)
})
It("should return err", func() {
err := client.ValidateCFUserCredentials()
Expect(err.Error()).Should(ContainSubstring("Invalid CloudFormation stack credentials"))
})
})

})
Context("EnsureOsdCcsAdminUser", func() {
var (
stackName string
Expand Down

0 comments on commit ba97dc0

Please sign in to comment.