Skip to content

Commit 2afa5c4

Browse files
committed
IR-412: IBMCloud: Add support for endpoint overrides
Add support to override IBM Cloud Service endpoints, when provided. Related: https://issues.redhat.com/browse/IR-412
1 parent 0c6fdc3 commit 2afa5c4

File tree

1 file changed

+122
-27
lines changed

1 file changed

+122
-27
lines changed

pkg/storage/ibmcos/ibmcos.go

Lines changed: 122 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"k8s.io/apimachinery/pkg/api/errors"
1616

1717
corev1 "k8s.io/api/core/v1"
18+
"k8s.io/klog/v2"
1819

1920
"github.com/IBM/go-sdk-core/v5/core"
2021
"github.com/IBM/ibm-cos-sdk-go/aws"
@@ -40,7 +41,12 @@ import (
4041
)
4142

4243
const (
43-
IAMEndpoint = "https://iam.cloud.ibm.com/identity/token"
44+
// IBMTokenPath is the URI path for the token endpoint
45+
IAMTokenPath = "/identity/token"
46+
// IAMEndpoint is the default IAM token endpoint
47+
IAMEndpoint = "https://iam.cloud.ibm.com/identity/token"
48+
49+
cosEndpointTemplate = "s3.%s.cloud-object-storage.appdomain.cloud"
4450
imageRegistrySecretDataKey = "credentials"
4551
imageRegistrySecretMountpoint = "/var/run/secrets/cloud"
4652
)
@@ -57,6 +63,12 @@ type driver struct {
5763
// IBM Services used only during tests.
5864
resourceController *resourcecontrollerv2.ResourceControllerV2
5965
resourceManager *resourcemanagerv2.ResourceManagerV2
66+
67+
// Endpoints to use for IBM Cloud Services
68+
iamServiceEndpoint string
69+
cosServiceEndpoint string
70+
rcServiceEndpoint string
71+
rmServiceEndpoint string
6072
}
6173

6274
// NewDriver creates a new IBM COS storage driver.
@@ -81,12 +93,18 @@ func (d *driver) ConfigEnv() (envs envvar.List, err error) {
8193
if err != nil {
8294
return
8395
}
96+
// Build the regional COS endpoint, or use the override endpoint if one was provided
97+
regionEndpoint := fmt.Sprintf(cosEndpointTemplate, d.Config.Location)
98+
if d.cosServiceEndpoint != "" {
99+
// We expect the override already is region specific
100+
regionEndpoint = d.cosServiceEndpoint
101+
}
84102

85103
envs = append(envs,
86104
envvar.EnvVar{Name: "REGISTRY_STORAGE", Value: "s3"},
87105
envvar.EnvVar{Name: "REGISTRY_STORAGE_S3_BUCKET", Value: d.Config.Bucket},
88106
envvar.EnvVar{Name: "REGISTRY_STORAGE_S3_REGION", Value: d.Config.Location},
89-
envvar.EnvVar{Name: "REGISTRY_STORAGE_S3_REGIONENDPOINT", Value: fmt.Sprintf("s3.%s.cloud-object-storage.appdomain.cloud", d.Config.Location)},
107+
envvar.EnvVar{Name: "REGISTRY_STORAGE_S3_REGIONENDPOINT", Value: regionEndpoint},
90108
envvar.EnvVar{Name: "REGISTRY_STORAGE_S3_ENCRYPT", Value: false},
91109
envvar.EnvVar{Name: "REGISTRY_STORAGE_S3_VIRTUALHOSTEDSTYLE", Value: false},
92110
envvar.EnvVar{Name: "REGISTRY_STORAGE_S3_USEDUALSTACK", Value: false},
@@ -122,6 +140,7 @@ func (d *driver) UpdateEffectiveConfig() (*imageregistryv1.ImageRegistryConfigSt
122140
}
123141
}
124142
}
143+
d.setServiceEndpointOverrides(infra)
125144

126145
// Use cluster defaults when custom config doesn't define values
127146
if d.Config == nil || (len(effectiveConfig.Location) == 0) {
@@ -163,7 +182,7 @@ func (d *driver) CreateStorage(cr *imageregistryv1.Config) error {
163182
}
164183

165184
// Get resource controller service
166-
rc, err := d.getResouceControllerService()
185+
rc, err := d.getResourceControllerService()
167186
if err != nil {
168187
return err
169188
}
@@ -237,7 +256,10 @@ func (d *driver) CreateStorage(cr *imageregistryv1.Config) error {
237256
},
238257
)
239258
if resourceGroups == nil || err != nil {
240-
return fmt.Errorf("unable to get resource groups: %s with resp code: %d", err.Error(), resp.StatusCode)
259+
if resp != nil {
260+
return fmt.Errorf("unable to get resource groups: %s with resp code: %d", err.Error(), resp.StatusCode)
261+
}
262+
return fmt.Errorf("unable to get resource groups: %w using ResourceManager endpoint: %s", err, rm.GetServiceURL())
241263
} else if len(resourceGroups.Resources) == 0 {
242264
return fmt.Errorf("unable to find any resource groups with resp code: %d", resp.StatusCode)
243265
}
@@ -444,6 +466,35 @@ func (d *driver) CreateStorage(cr *imageregistryv1.Config) error {
444466
return nil
445467
}
446468

469+
// setServiceEndpointOverrides will collect any necessary IBM Cloud Service endpoint overrides and set them for the driver to use for IBM Cloud Services
470+
func (d *driver) setServiceEndpointOverrides(infra *configapiv1.Infrastructure) {
471+
// We currently only handle overrides for IBMCloud (api/config/v1/IBMCloudPlatformType), not PowerVS
472+
if infra.Status.PlatformStatus != nil && infra.Status.PlatformStatus.Type == configapiv1.IBMCloudPlatformType && infra.Status.PlatformStatus.IBMCloud != nil {
473+
if len(infra.Status.PlatformStatus.IBMCloud.ServiceEndpoints) > 0 {
474+
for _, endpoint := range infra.Status.PlatformStatus.IBMCloud.ServiceEndpoints {
475+
switch endpoint.Name {
476+
case configapiv1.IBMCloudServiceCOS:
477+
klog.Infof("found override for ibmcloud cos endpoint: %s", endpoint.URL)
478+
d.cosServiceEndpoint = endpoint.URL
479+
case configapiv1.IBMCloudServiceIAM:
480+
klog.Infof("found override for ibmcloud iam endpoint: %s", endpoint.URL)
481+
d.iamServiceEndpoint = endpoint.URL
482+
case configapiv1.IBMCloudServiceResourceController:
483+
klog.Infof("found override for ibmcloud resource controller endpoint: %s", endpoint.URL)
484+
d.rcServiceEndpoint = endpoint.URL
485+
case configapiv1.IBMCloudServiceResourceManager:
486+
klog.Infof("found override for ibmcloud resource manager endpoint: %s", endpoint.URL)
487+
d.rmServiceEndpoint = endpoint.URL
488+
case configapiv1.IBMCloudServiceCIS, configapiv1.IBMCloudServiceDNSServices, configapiv1.IBMCloudServiceGlobalSearch, configapiv1.IBMCloudServiceGlobalTagging, configapiv1.IBMCloudServiceHyperProtect, configapiv1.IBMCloudServiceKeyProtect, configapiv1.IBMCloudServiceVPC:
489+
klog.Infof("ignoring unused service endpoint: %s", endpoint.Name)
490+
default:
491+
klog.Infof("ignoring unknown service: %s", endpoint.Name)
492+
}
493+
}
494+
}
495+
}
496+
}
497+
447498
// getAccountID returns the IBM Cloud account ID associated with the
448499
// IAM API key.
449500
func (d *driver) getAccountID() (string, error) {
@@ -456,6 +507,10 @@ func (d *driver) getAccountID() (string, error) {
456507
ApiKey: IAMAPIKey,
457508
}
458509

510+
if d.iamServiceEndpoint != "" {
511+
iamAuthenticator.URL = d.iamServiceEndpoint
512+
}
513+
459514
// Get IAM token
460515
iamToken, err := iamAuthenticator.RequestToken()
461516
if err != nil {
@@ -480,51 +535,81 @@ func (d *driver) getAccountID() (string, error) {
480535
return accountID, nil
481536
}
482537

483-
// getResouceControllerService returns the IBM Cloud resource controller
484-
// client.
485-
func (d *driver) getResouceControllerService() (*resourcecontrollerv2.ResourceControllerV2, error) {
538+
// getResourceControllerService returns the IBM Cloud resource controller client.
539+
func (d *driver) getResourceControllerService() (*resourcecontrollerv2.ResourceControllerV2, error) {
486540
if d.resourceController != nil {
487541
return d.resourceController, nil
488542
}
489543

544+
// Fetch the latest Infrastructure Status, for any endpoint changes
545+
infra, err := util.GetInfrastructure(d.Listers.Infrastructures)
546+
if err != nil {
547+
return nil, err
548+
}
549+
d.setServiceEndpointOverrides(infra)
550+
490551
IAMAPIKey, err := d.getCredentialsConfigData()
491552
if err != nil {
492553
return nil, err
493554
}
494555

495-
service, err := resourcecontrollerv2.NewResourceControllerV2(
496-
&resourcecontrollerv2.ResourceControllerV2Options{
497-
Authenticator: &core.IamAuthenticator{
498-
ApiKey: IAMAPIKey,
499-
},
500-
},
501-
)
556+
authenticator := &core.IamAuthenticator{
557+
ApiKey: IAMAPIKey,
558+
}
559+
560+
if d.iamServiceEndpoint != "" {
561+
authenticator.URL = d.iamServiceEndpoint
562+
}
563+
564+
rcOptions := &resourcecontrollerv2.ResourceControllerV2Options{
565+
Authenticator: authenticator,
566+
}
567+
if d.rcServiceEndpoint != "" {
568+
rcOptions.URL = d.rcServiceEndpoint
569+
}
570+
571+
service, err := resourcecontrollerv2.NewResourceControllerV2(rcOptions)
502572
if err != nil {
503573
return nil, err
504574
}
505575

506576
return service, nil
507577
}
508578

509-
// getResouceManagerService returns the IBM Cloud resource manager
510-
// client.
579+
// getResourceManagerService returns the IBM Cloud resource manager client.
511580
func (d *driver) getResourceManagerService() (*resourcemanagerv2.ResourceManagerV2, error) {
512581
if d.resourceManager != nil {
513582
return d.resourceManager, nil
514583
}
515584

585+
// Fetch the latest Infrastructure Status, for any endpoint changes
586+
infra, err := util.GetInfrastructure(d.Listers.Infrastructures)
587+
if err != nil {
588+
return nil, err
589+
}
590+
d.setServiceEndpointOverrides(infra)
591+
516592
IAMAPIKey, err := d.getCredentialsConfigData()
517593
if err != nil {
518594
return nil, err
519595
}
520596

521-
service, err := resourcemanagerv2.NewResourceManagerV2(
522-
&resourcemanagerv2.ResourceManagerV2Options{
523-
Authenticator: &core.IamAuthenticator{
524-
ApiKey: IAMAPIKey,
525-
},
526-
},
527-
)
597+
authenticator := &core.IamAuthenticator{
598+
ApiKey: IAMAPIKey,
599+
}
600+
601+
if d.iamServiceEndpoint != "" {
602+
authenticator.URL = d.iamServiceEndpoint
603+
}
604+
605+
rmOptions := &resourcemanagerv2.ResourceManagerV2Options{
606+
Authenticator: authenticator,
607+
}
608+
if d.rmServiceEndpoint != "" {
609+
rmOptions.URL = d.rmServiceEndpoint
610+
}
611+
612+
service, err := resourcemanagerv2.NewResourceManagerV2(rmOptions)
528613
if err != nil {
529614
return nil, err
530615
}
@@ -685,6 +770,7 @@ func (d *driver) bucketExists(bucketName string, serviceInstanceCRN string) erro
685770
// getIBMCOSClient returns a client that allows us to interact
686771
// with the IBM COS service.
687772
func (d *driver) getIBMCOSClient(serviceInstanceCRN string) (*s3.S3, error) {
773+
// Fetch the latest Infrastructure Status, for any endpoint changes
688774
infra, err := util.GetInfrastructure(d.Listers.Infrastructures)
689775
if err != nil {
690776
return nil, err
@@ -702,20 +788,29 @@ func (d *driver) getIBMCOSClient(serviceInstanceCRN string) (*s3.S3, error) {
702788
}
703789
}
704790
}
791+
d.setServiceEndpointOverrides(infra)
705792

706793
if IBMCOSLocation == "" {
707794
return nil, fmt.Errorf("unable to get location from infrastructure")
708795
}
709796

710-
serviceEndpoint := fmt.Sprintf("s3.%s.cloud-object-storage.appdomain.cloud", IBMCOSLocation)
797+
cosServiceEndpoint := fmt.Sprintf(cosEndpointTemplate, IBMCOSLocation)
798+
iamTokenEndpoint := IAMEndpoint
799+
if d.cosServiceEndpoint != "" {
800+
cosServiceEndpoint = d.cosServiceEndpoint
801+
}
802+
if d.iamServiceEndpoint != "" {
803+
iamTokenEndpoint = fmt.Sprintf("%s%s", d.iamServiceEndpoint, IAMTokenPath)
804+
}
805+
711806
IAMAPIKey, err := d.getCredentialsConfigData()
712807
if err != nil {
713808
return nil, err
714809
}
715810

716811
awsOptions := session.Options{
717812
Config: aws.Config{
718-
Endpoint: &serviceEndpoint,
813+
Endpoint: &cosServiceEndpoint,
719814
Region: &d.Config.Location,
720815
HTTPClient: &http.Client{
721816
Transport: &http.Transport{
@@ -742,7 +837,7 @@ func (d *driver) getIBMCOSClient(serviceInstanceCRN string) (*s3.S3, error) {
742837
awsOptions.Config.Credentials = credentials.AnonymousCredentials
743838
awsOptions.Config.HTTPClient.Transport = d.roundTripper
744839
} else {
745-
awsOptions.Config.Credentials = ibmiam.NewStaticCredentials(aws.NewConfig(), IAMEndpoint, IAMAPIKey, serviceInstanceCRN)
840+
awsOptions.Config.Credentials = ibmiam.NewStaticCredentials(aws.NewConfig(), iamTokenEndpoint, IAMAPIKey, serviceInstanceCRN)
746841
}
747842

748843
sess, err := session.NewSessionWithOptions(awsOptions)
@@ -791,7 +886,7 @@ func (d *driver) VolumeSecrets() (map[string]string, error) {
791886
}
792887

793888
// Get resource controller service
794-
rc, err := d.getResouceControllerService()
889+
rc, err := d.getResourceControllerService()
795890
if err != nil {
796891
return nil, err
797892
}

0 commit comments

Comments
 (0)