-
Notifications
You must be signed in to change notification settings - Fork 186
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
OCM-7647 | test: automate id:OCP-72899 create/list/describe/delete br…
…eak_glass_credentials
- Loading branch information
Showing
4 changed files
with
327 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package e2e | ||
|
||
import ( | ||
"time" | ||
|
||
. "github.com/onsi/ginkgo/v2" | ||
. "github.com/onsi/gomega" | ||
|
||
"github.com/openshift/rosa/tests/ci/labels" | ||
"github.com/openshift/rosa/tests/utils/common" | ||
"github.com/openshift/rosa/tests/utils/exec/rosacli" | ||
) | ||
|
||
var _ = Describe("Create Tuning Config", func() { | ||
|
||
var rosaClient *rosacli.Client | ||
var clusterService rosacli.ClusterService | ||
|
||
BeforeEach(func() { | ||
Expect(clusterID).ToNot(BeEmpty(), "Cluster ID is empty, please export the env variable CLUSTER_ID") | ||
rosaClient = rosacli.NewClient() | ||
clusterService = rosaClient.Cluster | ||
|
||
By("Check cluster is hosted cluster and external auth config is enabled") | ||
hosted, err := clusterService.IsHostedCPCluster(clusterID) | ||
Expect(err).ToNot(HaveOccurred()) | ||
externalAuthProvider, err := clusterService.IsExternalAuthenticationEnabled(clusterID) | ||
Expect(err).ToNot(HaveOccurred()) | ||
if !hosted || !externalAuthProvider { | ||
Skip("This is only for external_auth_config enabled hypershift cluster") | ||
} | ||
}) | ||
|
||
AfterEach(func() { | ||
By("Clean remaining resources") | ||
err := rosaClient.CleanResources(clusterID) | ||
Expect(err).ToNot(HaveOccurred()) | ||
}) | ||
|
||
It("create/list/describe/delete HCP cluster with break_glass_credentials via Rosa client can work well - [id:72899]", | ||
labels.High, labels.NonClassicCluster, | ||
func() { | ||
By("Retrieve help for create/list/describe/delete break-glass-credential") | ||
_, err := rosaClient.BreakGlassCredential.RetrieveHelpForCreate() | ||
Expect(err).ToNot(HaveOccurred()) | ||
_, err = rosaClient.BreakGlassCredential.RetrieveHelpForDescribe() | ||
Expect(err).ToNot(HaveOccurred()) | ||
_, err = rosaClient.BreakGlassCredential.RetrieveHelpForList() | ||
Expect(err).ToNot(HaveOccurred()) | ||
_, err = rosaClient.BreakGlassCredential.RetrieveHelpForDelete() | ||
Expect(err).ToNot(HaveOccurred()) | ||
|
||
By("Create a break-glass-credential to the cluster") | ||
userName := common.GenerateRandomName("bgc-username", 2) | ||
createTime := time.Now().UTC() | ||
expiredTime := createTime.Add(2 * time.Hour).Format("Jan _2 2006 15:04 MST") | ||
|
||
resp, err := rosaClient.BreakGlassCredential.CreateBreakGlassCredential(clusterID, userName, "--expiration", "2h") | ||
Expect(err).ToNot(HaveOccurred()) | ||
textData := rosaClient.Parser.TextData.Input(resp).Parse().Tip() | ||
Expect(textData).To(ContainSubstring("INFO: Successfully created a break glass credential for cluster '%s'", clusterID)) | ||
|
||
By("List the break-glass-credentials of the cluster") | ||
breakGlassCredList, err := rosaClient.BreakGlassCredential.ListBreakGlassCredentialsAndReflect(clusterID) | ||
Expect(err).ToNot(HaveOccurred()) | ||
isPresent, breakGlassCredential := breakGlassCredList.IsPresent(userName) | ||
Expect(isPresent).To(BeTrue()) | ||
Eventually(rosaClient.BreakGlassCredential.WaitForBreakGlassCredentialToStatus(clusterID, "issued", userName), time.Minute*2, time.Second*10).Should(BeTrue()) | ||
|
||
By("Retrieve the break-glass-credential id") | ||
breakGlassCredID := breakGlassCredential.ID | ||
|
||
By("Describe the break-glass-credential") | ||
output, err := rosaClient.BreakGlassCredential.DescribeBreakGlassCredentialsAndReflect(clusterID, breakGlassCredID) | ||
Expect(err).ToNot(HaveOccurred()) | ||
// expiration timestamp changes from 4 to 5 seconds ahead, so have to remove second from time stamp | ||
expirationTime, err := time.Parse("Jan _2 2006 15:04:05 MST", output.ExpireAt) | ||
Expect(err).ToNot(HaveOccurred()) | ||
Expect(output.ID).To(Equal(breakGlassCredID)) | ||
Expect(expirationTime.Format("Jan _2 2006 15:04 MST")).To(Equal(expiredTime)) | ||
Expect(output.Username).To(Equal(userName)) | ||
Expect(output.Status).To(Equal("issued")) | ||
|
||
By("Get the issued credential") | ||
_, err = rosaClient.BreakGlassCredential.GetIssuedCredential(clusterID, breakGlassCredID) | ||
Expect(err).ToNot(HaveOccurred()) | ||
|
||
By("Delete the break-glass-credential") | ||
resp, err = rosaClient.BreakGlassCredential.DeleteBreakGlassCredential(clusterID) | ||
Expect(err).ToNot(HaveOccurred()) | ||
textData = rosaClient.Parser.TextData.Input(resp).Parse().Tip() | ||
Expect(textData).To(ContainSubstring("INFO: Successfully requested revocation for all break glass credentials from cluster '%s'", clusterID)) | ||
|
||
By("Check the break-glass-credential status is revoked") | ||
Eventually(rosaClient.BreakGlassCredential.WaitForBreakGlassCredentialToStatus(clusterID, "revoked", userName), time.Minute*4, time.Second*10).Should(BeTrue()) | ||
}) | ||
}) |
205 changes: 205 additions & 0 deletions
205
tests/utils/exec/rosacli/break_glass_credential_service.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
package rosacli | ||
|
||
import ( | ||
"bytes" | ||
|
||
"gopkg.in/yaml.v3" | ||
|
||
"k8s.io/apimachinery/pkg/util/wait" | ||
. "github.com/openshift/rosa/tests/utils/log" | ||
) | ||
|
||
type BreakGlassCredentialService interface { | ||
ResourcesCleaner | ||
|
||
CreateBreakGlassCredential(clusterID string, userName string, flags ...string) (bytes.Buffer, error) | ||
DeleteBreakGlassCredential(clusterID string) (bytes.Buffer, error) | ||
ListBreakGlassCredentials(clusterID string) (bytes.Buffer, error) | ||
ReflectBreakGlassCredentialLists(result bytes.Buffer) (bgcl *BreakGlassCredentialList, err error) | ||
DescribeBreakGlassCredential(clusterID string, bgcID string) (bytes.Buffer, error) | ||
ReflectBreakGlassCredentialDescription(result bytes.Buffer) (bgcd *BreakGlassCredentialDescription, err error) | ||
GetIssuedCredential(clusterID string, bgcID string) (bytes.Buffer, error) | ||
WaitForBreakGlassCredentialToStatus(clusterID string, status string, userName string) wait.ConditionFunc | ||
|
||
ListBreakGlassCredentialsAndReflect(clusterID string) (*BreakGlassCredentialList, error) | ||
DescribeBreakGlassCredentialsAndReflect(clusterID string, bgcID string) (*BreakGlassCredentialDescription, error) | ||
|
||
RetrieveHelpForCreate() (bytes.Buffer, error) | ||
RetrieveHelpForList() (bytes.Buffer, error) | ||
RetrieveHelpForDescribe() (bytes.Buffer, error) | ||
RetrieveHelpForDelete() (bytes.Buffer, error) | ||
} | ||
|
||
type breakglasscredentialService struct { | ||
ResourcesService | ||
} | ||
|
||
type BreakGlassCredential struct { | ||
ID string `json:"ID,omitempty"` | ||
Username string `json:"Username,omitempty"` | ||
Status string `json:"Status,omitempty"` | ||
} | ||
|
||
type BreakGlassCredentialList struct { | ||
BreakGlassCredentials []BreakGlassCredential `json:"BreakGlassCredentials,omitempty"` | ||
} | ||
|
||
type BreakGlassCredentialDescription struct { | ||
ID string `yaml:"ID,omitempty"` | ||
Username string `yaml:"Username,omitempty"` | ||
ExpireAt string `yaml:"Expire at,omitempty"` | ||
Status string `yaml:"Status,omitempty"` | ||
} | ||
|
||
func NewBreakGlassCredentialService(client *Client) BreakGlassCredentialService { | ||
return &breakglasscredentialService{ | ||
ResourcesService: ResourcesService{ | ||
client: client, | ||
}, | ||
} | ||
} | ||
|
||
// Create BreakGlassCredential | ||
func (b *breakglasscredentialService) CreateBreakGlassCredential(clusterID string, userName string, flags ...string) (output bytes.Buffer, err error) { | ||
output, err = b.client.Runner. | ||
Cmd("create", "break-glass-credential"). | ||
CmdFlags(append(flags, "-c", clusterID, "--username", userName)...). | ||
Run() | ||
return | ||
} | ||
|
||
// List BreakGlassCredentials | ||
func (b *breakglasscredentialService) ListBreakGlassCredentials(clusterID string) (output bytes.Buffer, err error) { | ||
output, err = b.client.Runner. | ||
Cmd("list", "break-glass-credential"). | ||
CmdFlags("-c", clusterID). | ||
Run() | ||
return | ||
} | ||
|
||
// Check the breakGlassCredential with the userName exists in the breakGlassCredentialsList | ||
func (breakglassCredList BreakGlassCredentialList) IsPresent(userName string) (existed bool, breakGlassCredential *BreakGlassCredential) { | ||
existed = false | ||
breakGlassCredential = &BreakGlassCredential{} | ||
for _, breakGlassCred := range breakglassCredList.BreakGlassCredentials { | ||
if breakGlassCred.Username == userName { | ||
existed = true | ||
breakGlassCredential = &breakGlassCred | ||
break | ||
} | ||
} | ||
return | ||
} | ||
|
||
func (b *breakglasscredentialService) ReflectBreakGlassCredentialLists(result bytes.Buffer) (bgcl *BreakGlassCredentialList, err error) { | ||
bgcl = &BreakGlassCredentialList{} | ||
theMap := b.client.Parser.TableData.Input(result).Parse().Output() | ||
for _, bgcItem := range theMap { | ||
breakGlassCredential := &BreakGlassCredential{} | ||
err = MapStructure(bgcItem, breakGlassCredential) | ||
if err != nil { | ||
return | ||
} | ||
bgcl.BreakGlassCredentials = append(bgcl.BreakGlassCredentials, *breakGlassCredential) | ||
} | ||
return | ||
} | ||
|
||
func (b *breakglasscredentialService) ListBreakGlassCredentialsAndReflect(clusterID string) (*BreakGlassCredentialList, error) { | ||
output, err := b.ListBreakGlassCredentials(clusterID) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return b.ReflectBreakGlassCredentialLists(output) | ||
} | ||
|
||
// Describe BreakGlassCredential | ||
func (b *breakglasscredentialService) DescribeBreakGlassCredential(clusterID string, bgcID string) (bytes.Buffer, error) { | ||
describe := b.client.Runner. | ||
Cmd("describe", "break-glass-credential", bgcID). | ||
CmdFlags("-c", clusterID) | ||
|
||
return describe.Run() | ||
} | ||
|
||
func (b *breakglasscredentialService) DescribeBreakGlassCredentialsAndReflect(clusterID string, bgcID string) (*BreakGlassCredentialDescription, error) { | ||
output, err := b.DescribeBreakGlassCredential(clusterID, bgcID) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return b.ReflectBreakGlassCredentialDescription(output) | ||
} | ||
|
||
func (b *breakglasscredentialService) ReflectBreakGlassCredentialDescription(result bytes.Buffer) (bgcd *BreakGlassCredentialDescription, err error) { | ||
var data []byte | ||
res := &BreakGlassCredentialDescription{} | ||
theMap, err := b.client.Parser.TextData.Input(result).Parse().YamlToMap() | ||
if err != nil { | ||
return | ||
} | ||
data, err = yaml.Marshal(&theMap) | ||
if err != nil { | ||
return | ||
} | ||
err = yaml.Unmarshal(data, res) | ||
return res, err | ||
} | ||
|
||
// Delete BreakGlassCredential | ||
func (b *breakglasscredentialService) DeleteBreakGlassCredential(clusterID string) (output bytes.Buffer, err error) { | ||
output, err = b.client.Runner. | ||
Cmd("revoke", "break-glass-credential"). | ||
CmdFlags("-c", clusterID, "-y"). | ||
Run() | ||
return | ||
} | ||
|
||
func (b *breakglasscredentialService) GetIssuedCredential(clusterID string, bgcID string) (bytes.Buffer, error) { | ||
output, err := b.client.Runner. | ||
Cmd("describe", "break-glass-credential"). | ||
CmdFlags("-c", clusterID, "--id", bgcID, "--kubeconfig"). | ||
Run() | ||
return output, err | ||
} | ||
|
||
func (b *breakglasscredentialService) WaitForBreakGlassCredentialToStatus(clusterID string, status string, userName string) wait.ConditionFunc { | ||
return func() (bool, error) { | ||
breakGlassCredList, err := b.ListBreakGlassCredentialsAndReflect(clusterID) | ||
if err != nil { | ||
return false, err | ||
} | ||
_, breakGlassCredential := breakGlassCredList.IsPresent(userName) | ||
Logger.Infof("The status for break-glass-credential %s is %s\n", breakGlassCredential.ID, breakGlassCredential.Status) | ||
return breakGlassCredential.Status == status, err | ||
} | ||
} | ||
|
||
// Help for Create BreakGlassCredential | ||
func (b *breakglasscredentialService) RetrieveHelpForCreate() (output bytes.Buffer, err error) { | ||
return b.client.Runner.Cmd("create", "break-glass-credential").CmdFlags("-h").Run() | ||
} | ||
|
||
// Help for List BreakGlassCredential | ||
func (b *breakglasscredentialService) RetrieveHelpForList() (output bytes.Buffer, err error) { | ||
return b.client.Runner.Cmd("list", "break-glass-credential").CmdFlags("-h").Run() | ||
} | ||
|
||
// Help for Describe BreakGlassCredential | ||
func (b *breakglasscredentialService) RetrieveHelpForDescribe() (output bytes.Buffer, err error) { | ||
return b.client.Runner.Cmd("describe", "break-glass-credential").CmdFlags("-h").Run() | ||
} | ||
|
||
// Help for Delete BreakGlassCredential | ||
func (b *breakglasscredentialService) RetrieveHelpForDelete() (output bytes.Buffer, err error) { | ||
return b.client.Runner.Cmd("revoke", "break-glass-credential").CmdFlags("-h").Run() | ||
} | ||
|
||
func (b *breakglasscredentialService) CleanResources(clusterID string) (errors []error) { | ||
Logger.Infof("Remove remaining break-glass-credentials") | ||
_, err := b.DeleteBreakGlassCredential(clusterID) | ||
if err != nil { | ||
errors = append(errors, err) | ||
} | ||
|
||
return | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters