Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions cmd/create/network/hyperfleet.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,13 @@ func runHyperfleetCreateNetwork(r *rosa.Runtime, userOptions *opts.NetworkUserOp
case interactive.ModeManual:
r.Reporter.Infof(helper.ManualModeHelperMessage(parsedParams, parsedTags))
r.Reporter.Infof("\nTo create the network stack manually, save the template below and use:")
r.Reporter.Infof(" aws cloudformation create-stack --stack-name %s --template-body file://template.yaml --parameters ...", parsedParams["Name"])
r.Reporter.Infof(
" aws cloudformation create-stack --stack-name %s --template-body file://template.yaml --parameters ...",
parsedParams["Name"],
)
fmt.Println("\nCloudFormation Template:")
fmt.Println("---")
fmt.Println(CloudFormationHCPTemplateFile)
fmt.Print(CloudFormationHCPTemplateFile)
return

default:
Expand All @@ -101,6 +104,9 @@ func runHyperfleetCreateNetwork(r *rosa.Runtime, userOptions *opts.NetworkUserOp

r.Reporter.Infof("Network stack created successfully")
r.Reporter.Infof("Use the following to get stack outputs:")
r.Reporter.Infof(" aws cloudformation describe-stacks --stack-name %s --query 'Stacks[0].Outputs'", parsedParams["Name"])
r.Reporter.Infof(
" aws cloudformation describe-stacks --stack-name %s --query 'Stacks[0].Outputs'",
parsedParams["Name"],
)
}
}
9 changes: 0 additions & 9 deletions cmd/create/oidcconfig/hyperfleet.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,6 @@ var (
}
)

// runHyperfleetCreate is a thin wrapper for direct test invocation without a real cobra.Command.
func runHyperfleetCreate(r *rosa.Runtime) {
if err := hfpathbind.RunCreateOidcConfig(context.Background(), r, nil, &hfOidcConfigInput,
&hyperfleetOidcConfigCreate{},
); err != nil {
hfExitFn(1)
}
}

// hyperfleetOidcConfigCreate implements hfpathbind.OidcConfigCreateHandler for rosa create oidcconfig.
type hyperfleetOidcConfigCreate struct {
// interactive prompting for required fields
Expand Down
22 changes: 18 additions & 4 deletions cmd/create/operatorroles/by_prefix_hyperfleet.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ func runHyperfleetCreateOperatorRoles(r *rosa.Runtime) {
var trustPolicy string
if spec.IsWorkerRole {
// EC2 trust policy for worker role
trustPolicy = fmt.Sprintf(`{
trustPolicy = `{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
Expand All @@ -198,7 +198,7 @@ func runHyperfleetCreateOperatorRoles(r *rosa.Runtime) {
},
"Action": "sts:AssumeRole"
}]
}`)
}`
} else {
// OIDC trust policy for operator roles
trustPolicy = fmt.Sprintf(`{
Expand All @@ -216,7 +216,14 @@ func runHyperfleetCreateOperatorRoles(r *rosa.Runtime) {
}
}
}]
}`, r.Creator.Partition, r.Creator.AccountID, oidcIssuerDomain, oidcIssuerDomain, spec.ServiceAccount, oidcIssuerDomain)
}`,
r.Creator.Partition,
r.Creator.AccountID,
oidcIssuerDomain,
oidcIssuerDomain,
spec.ServiceAccount,
oidcIssuerDomain,
)
}

r.Reporter.Debugf("Creating role '%s'", roleName)
Expand Down Expand Up @@ -303,7 +310,14 @@ func runHyperfleetCreateOperatorRoles(r *rosa.Runtime) {
}
}
}]
}'`, r.Creator.Partition, r.Creator.AccountID, oidcIssuerDomain, oidcIssuerDomain, spec.ServiceAccount, oidcIssuerDomain)
}'`,
r.Creator.Partition,
r.Creator.AccountID,
oidcIssuerDomain,
oidcIssuerDomain,
spec.ServiceAccount,
oidcIssuerDomain,
)
}

// Build tags
Expand Down
6 changes: 1 addition & 5 deletions cmd/list/operatorroles/hyperfleet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ var _ = Describe("hyperfleet dispatch", func() {
origHfListOperatorRoles func()

enabledCalled bool
exitCode *int
listCalled bool
)

Expand All @@ -22,16 +21,13 @@ var _ = Describe("hyperfleet dispatch", func() {
origHfListOperatorRoles = hfListOperatorRoles

enabledCalled = false
exitCode = nil
listCalled = false

hfEnabled = func() bool {
enabledCalled = true
return true
}
hfExitFn = func(code int) {
exitCode = &code
}
hfExitFn = func(int) {}
hfListOperatorRoles = func() {
listCalled = true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@

# hyperfleet only commands
# currently manually set, maybe generate as part of generate-hyperfleet?
- name: "platform"
- name: "provisioner-config-name"
- name: "delete-protection"
- name: "display-name"
- name: "container-log-max-files"
Expand All @@ -137,7 +139,6 @@
- name: "system-reserved"
- name: "allowed-kernel-arguments"
- name: "image-content-sources"
- name: "issuer-url"
- name: "allocate-node-cidrs"
- name: "advertise-address"
- name: "allowed-cidr-blocks"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@

# hyperfleet only commands
# currently manually set, maybe generate as part of generate-hyperfleet?
- name: "role-arn"
- name: "platform"
- name: "provisioner-config-name"
- name: "delete-protection"
- name: "display-name"
- name: "container-log-max-files"
Expand All @@ -45,7 +48,6 @@
- name: "serialize-image-pulls"
- name: "streaming-connection-idle-timeout"
- name: "image-content-sources"
- name: "issuer-url"
- name: "allocate-node-cidrs"
- name: "advertise-address"
- name: "allowed-cidr-blocks"
Expand Down
4 changes: 2 additions & 2 deletions cmd/whoami/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ var _ = Describe("whoami command", func() {
Expect(stdout).To(ContainSubstring("OCM Account Username:"))
})

It("Shows Platform API URL when logged in with hyperfleet only (no OCM)", func() {
It("Shows V2 API URL when logged in with hyperfleet only (no OCM)", func() {
hfURL := "https://test.execute-api.us-east-1.amazonaws.com/prod"
saveConfig(&config.Config{HyperfleetURL: hfURL})

Expand All @@ -331,7 +331,7 @@ var _ = Describe("whoami command", func() {
return runWithRuntime(r)
}, t.RosaRuntime, Cmd)
Expect(err).NotTo(HaveOccurred())
Expect(stdout).To(ContainSubstring("Platform API"))
Expect(stdout).To(ContainSubstring("V2 API"))
Expect(stdout).To(ContainSubstring(hfURL))
Expect(stdout).NotTo(ContainSubstring("OCM API"))
})
Expand Down
7 changes: 6 additions & 1 deletion pkg/aws/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,12 @@ type Client interface {
GetOpenIDConnectProviderByClusterIdTag(clusterID string) (string, error)
GetOpenIDConnectProviderByOidcEndpointUrl(oidcEndpointUrl string) (string, error)
GetInstanceProfilesForRole(role string) ([]string, error)
EnsureInstanceProfile(reporter reporter.Logger, instanceProfileName string, roleName string, tagList map[string]string) error
EnsureInstanceProfile(
reporter reporter.Logger,
instanceProfileName string,
roleName string,
tagList map[string]string,
) error
IsUpgradedNeededForAccountRolePolicies(rolePrefix string, version string) (bool, error)
IsUpgradedNeededForAccountRolePoliciesUsingCluster(clusterID *cmv1.Cluster, version string) (bool, error)
IsUpgradedNeededForOperatorRolePoliciesUsingCluster(
Expand Down
7 changes: 6 additions & 1 deletion pkg/aws/policies.go
Original file line number Diff line number Diff line change
Expand Up @@ -1223,7 +1223,12 @@ func (c *awsClient) GetInstanceProfilesForRole(r string) ([]string, error) {

// EnsureInstanceProfile creates an instance profile if it doesn't exist and associates it with the role.
// For worker roles, the instance profile must exist for EC2 instances to assume the IAM role.
func (c *awsClient) EnsureInstanceProfile(reporter reporter.Logger, instanceProfileName string, roleName string, tagList map[string]string) error {
func (c *awsClient) EnsureInstanceProfile(
reporter reporter.Logger,
instanceProfileName string,
roleName string,
tagList map[string]string,
) error {
// Check if instance profile already exists
_, err := c.iamClient.GetInstanceProfile(context.Background(), &iam.GetInstanceProfileInput{
InstanceProfileName: aws.String(instanceProfileName),
Expand Down
155 changes: 155 additions & 0 deletions pkg/hyperfleet/pathbind/cluster_create_normalize_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package pathbind

import (
"context"
"testing"

v1alpha1 "github.com/openshift-online/rosa-hyperfleet-api/api/v1alpha1/public"
"github.com/openshift-online/rosa-hyperfleet-api/clientset/pathbind"
"github.com/spf13/cobra"
)

type clusterCreateBoolField struct {
flag string
set func(*ClusterCreateInput, *bool)
get func(*ClusterCreateInput) *bool
}

var clusterCreateBoolFields = []clusterCreateBoolField{
{
flag: "delete-protection",
set: func(i *ClusterCreateInput, p *bool) { i.DeleteProtection = p },
get: func(i *ClusterCreateInput) *bool { return i.DeleteProtection },
},
{
flag: "serialize-image-pulls",
set: func(i *ClusterCreateInput, p *bool) { i.SerializeImagePulls = p },
get: func(i *ClusterCreateInput) *bool { return i.SerializeImagePulls },
},
{
flag: "multi-arch",
set: func(i *ClusterCreateInput, p *bool) { i.MultiArch = p },
get: func(i *ClusterCreateInput) *bool { return i.MultiArch },
},
}

type clusterUpdateBoolField struct {
flag string
set func(*ClusterUpdateInput, *bool)
get func(*ClusterUpdateInput) *bool
}

var clusterUpdateBoolFields = []clusterUpdateBoolField{
{
flag: "delete-protection",
set: func(i *ClusterUpdateInput, p *bool) { i.DeleteProtection = p },
get: func(i *ClusterUpdateInput) *bool { return i.DeleteProtection },
},
{
flag: "serialize-image-pulls",
set: func(i *ClusterUpdateInput, p *bool) { i.SerializeImagePulls = p },
get: func(i *ClusterUpdateInput) *bool { return i.SerializeImagePulls },
},
{
flag: "multi-arch",
set: func(i *ClusterUpdateInput, p *bool) { i.MultiArch = p },
get: func(i *ClusterUpdateInput) *bool { return i.MultiArch },
},
}

func TestNormalizeClusterCreateInput_clearsUnsetBoolFlags(t *testing.T) {
for _, field := range clusterCreateBoolFields {
t.Run(field.flag, func(t *testing.T) {
cmd := &cobra.Command{}
cmd.Flags().Bool(field.flag, false, "")

input := &ClusterCreateInput{}
field.set(input, new(bool))
normalizeClusterCreateInput(cmd, input)

if field.get(input) != nil {
t.Fatalf("expected nil %s, got %v", field.flag, *field.get(input))
}
})
}
}

func TestNormalizeClusterCreateInput_keepsChangedBoolFlags(t *testing.T) {
for _, field := range clusterCreateBoolFields {
t.Run(field.flag, func(t *testing.T) {
cmd := &cobra.Command{}
cmd.Flags().Bool(field.flag, false, "")
if err := cmd.Flags().Set(field.flag, "true"); err != nil {
t.Fatalf("Set flag: %v", err)
}

input := &ClusterCreateInput{}
v := true
field.set(input, &v)
normalizeClusterCreateInput(cmd, input)

got := field.get(input)
if got == nil || !*got {
t.Fatalf("expected %s=true after explicit flag set", field.flag)
}
})
}
}

func TestNormalizeClusterCreateInput_clearsUnsetSerializeImagePullsFromExpand(t *testing.T) {
cmd := &cobra.Command{}
cmd.Flags().Bool("serialize-image-pulls", false, "")

input := &ClusterCreateInput{
SerializeImagePulls: new(bool),
}
normalizeClusterCreateInput(cmd, input)

obj := &v1alpha1.Cluster{}
if err := pathbind.Expand(context.Background(), *input, obj); err != nil {
t.Fatalf("Expand failed: %v", err)
}
cfg := obj.Spec.HostedCluster.Configuration
if cfg != nil && cfg.Kubelet != nil && cfg.Kubelet.SerializeImagePulls != nil {
t.Fatal("expected serializeImagePulls omitted from expanded cluster spec")
}
}

func TestNormalizeClusterUpdateInput_clearsUnsetBoolFlags(t *testing.T) {
for _, field := range clusterUpdateBoolFields {
t.Run(field.flag, func(t *testing.T) {
cmd := &cobra.Command{}
cmd.Flags().Bool(field.flag, false, "")

input := &ClusterUpdateInput{}
field.set(input, new(bool))
normalizeClusterUpdateInput(cmd, input)

if field.get(input) != nil {
t.Fatalf("expected nil %s, got %v", field.flag, *field.get(input))
}
})
}
}

func TestNormalizeClusterUpdateInput_keepsChangedBoolFlags(t *testing.T) {
for _, field := range clusterUpdateBoolFields {
t.Run(field.flag, func(t *testing.T) {
cmd := &cobra.Command{}
cmd.Flags().Bool(field.flag, false, "")
if err := cmd.Flags().Set(field.flag, "true"); err != nil {
t.Fatalf("Set flag: %v", err)
}

input := &ClusterUpdateInput{}
v := true
field.set(input, &v)
normalizeClusterUpdateInput(cmd, input)

got := field.get(input)
if got == nil || !*got {
t.Fatalf("expected %s=true after explicit flag set", field.flag)
}
})
}
}
3 changes: 3 additions & 0 deletions pkg/rosa/runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ var _ = Describe("WithHyperFleet", func() {
})

It("exits when URL has no extractable region", func() {
GinkgoT().Setenv("AWS_REGION", "")
GinkgoT().Setenv("AWS_DEFAULT_REGION", "")

hfExplicitURL = func() string { return "https://example.com/api" }

r := &Runtime{Reporter: fakeRept}
Expand Down