@@ -19,46 +19,69 @@ import (
1919 "strings"
2020 "time"
2121
22- "github.com/IBM-Cloud/bluemix-go/api/resource/resourcev1/controller"
23- "github.com/IBM-Cloud/bluemix-go/api/resource/resourcev2/controllerv2"
24- "github.com/IBM-Cloud/bluemix-go/crn"
25- "github.com/IBM-Cloud/bluemix-go/models"
2622 pmodels "github.com/IBM-Cloud/power-go-client/power/models"
23+ "github.com/IBM/platform-services-go-sdk/resourcecontrollerv2"
2724 "github.com/spf13/cobra"
2825 "k8s.io/klog/v2"
26+ "k8s.io/utils/ptr"
2927
3028 "github.com/ppc64le-cloud/pvsadm/pkg"
3129 "github.com/ppc64le-cloud/pvsadm/pkg/client"
3230 "github.com/ppc64le-cloud/pvsadm/pkg/utils"
3331)
3432
3533const (
34+ accessKeyId = "access_key_id"
35+ cosHmacKeys = "cos_hmac_keys"
36+ crnServiceRoleWriter = "crn:v1:bluemix:public:iam::::serviceRole:Writer"
37+ imageStateActive = "active"
38+ jobStateCompleted = "completed"
39+ jobStateFailed = "failed"
40+ secretAccessKey = "secret_access_key"
41+ // CosResourceID is IBM COS service id, can be retrieved using ibmcloud cli
42+ // ibmcloud catalog service cloud-object-storage.
3643 serviceCredPrefix = "pvsadm-service-cred"
37- imageStateActive = "active"
38- jobStateCompleted = "completed"
39- jobStateFailed = "failed"
4044)
4145
42- // Find COSINSTANCE details of the Provided bucket
43- func findCOSInstanceDetails (resources []models. ServiceInstanceV2 , bxCli * client.Client ) ( string , string , crn. CRN ) {
46+ // findCOSInstance retrieves the service instance in which the bucket is present.
47+ func findCOSInstanceDetails (resources []resourcecontrollerv2. ResourceInstance , pvsClient * client.Client ) * resourcecontrollerv2. ResourceInstance {
4448 for _ , resource := range resources {
45- if resource . Crn . ServiceName == "cloud-object-storage" {
46- s3client , err := client . NewS3Client ( bxCli , resource . Name , pkg . ImageCMDOptions . Region )
47- if err != nil {
48- continue
49- }
50- buckets , err := s3client .S3Session .ListBuckets (nil )
51- if err != nil {
52- continue
53- }
54- for _ , bucket := range buckets . Buckets {
55- if * bucket . Name == pkg . ImageCMDOptions . BucketName {
56- return resource .Name , resource . Guid , resource . Crn
57- }
49+ s3client , err := client . NewS3Client ( pvsClient , * resource . Name , pkg . ImageCMDOptions . Region )
50+ if err != nil {
51+ klog . Warningf ( "cannot create a new s3 client. err: %v" , err )
52+ continue
53+ }
54+ buckets , err := s3client .S3Session .ListBuckets (nil )
55+ if err != nil {
56+ klog . Warningf ( "cannot list buckets in the resource instance. err: %v" , err )
57+ continue
58+ }
59+ for _ , bucket := range buckets . Buckets {
60+ if * bucket .Name == pkg . ImageCMDOptions . BucketName {
61+ return & resource
5862 }
5963 }
6064 }
61- return "" , "" , crn.CRN {}
65+ return nil
66+ }
67+
68+ // createNewCredentialsWithHMAC generates the service credentials in the given COS instance with HMAC keys.
69+ func createNewCredentialsWithHMAC (pvsClient * client.Client , cosCRN , serviceCredName string ) (* resourcecontrollerv2.ResourceKey , error ) {
70+ klog .V (2 ).Infof ("Auto generating COS service credentials to import image: %s" , serviceCredName )
71+ params := & resourcecontrollerv2.ResourceKeyPostParameters {}
72+ params .SetProperty ("HMAC" , true )
73+ key , _ , err := pvsClient .ResourceControllerClient .CreateResourceKey (
74+ & resourcecontrollerv2.CreateResourceKeyOptions {
75+ Name : ptr .To (serviceCredName ),
76+ Parameters : params ,
77+ Role : ptr .To (crnServiceRoleWriter ),
78+ Source : ptr .To (cosCRN ),
79+ },
80+ )
81+ if err != nil {
82+ return nil , fmt .Errorf ("unable to create resource key for service instance: %v" , err .Error ())
83+ }
84+ return key , nil
6285}
6386
6487// checkStorageTierAvailability confirms if the provided cloud instance ID supports the required storageType.
@@ -95,9 +118,9 @@ pvsadm image import --help for information
95118# Set the API key or feed the --api-key commandline argument
96119export IBMCLOUD_API_KEY=<IBM_CLOUD_API_KEY>
97120
98- # To Import the image across the two different IBM account use accesskey and secretkey options
121+ # To Import the image across the two different IBM account use "-- accesskey" and "-- secretkey" options
99122
100- # To Import the image from public bucket use public-bucket option
123+ # To Import the image from public bucket use the "-- public-bucket" or "-p" option
101124
102125Examples:
103126
@@ -132,12 +155,12 @@ pvsadm image import -n upstream-core-lon04 -b <BUCKETNAME> --object rhel-83-1003
132155 opt := pkg .ImageCMDOptions
133156 apikey := pkg .Options .APIKey
134157
135- bxCli , err := client .NewClientWithEnv (apikey , pkg .Options .Environment , pkg .Options .Debug )
158+ pvsClient , err := client .NewClientWithEnv (apikey , pkg .Options .Environment , pkg .Options .Debug )
136159 if err != nil {
137160 return err
138161 }
139162
140- pvmclient , err := client .NewPVMClientWithEnv (bxCli , opt .WorkspaceID , opt .WorkspaceName , pkg .Options .Environment )
163+ pvmclient , err := client .NewPVMClientWithEnv (pvsClient , opt .WorkspaceID , opt .WorkspaceName , pkg .Options .Environment )
141164 if err != nil {
142165 return err
143166 }
@@ -148,54 +171,87 @@ pvsadm image import -n upstream-core-lon04 -b <BUCKETNAME> --object rhel-83-1003
148171
149172 //Create AccessKey and SecretKey for the bucket provided if bucket access is private
150173 if (opt .AccessKey == "" || opt .SecretKey == "" ) && (! opt .Public ) {
151- //Find CosInstance of the bucket
152- var svcs []models.ServiceInstanceV2
153- svcs , err = bxCli .ResourceClientV2 .ListInstances (controllerv2.ServiceInstanceQuery {
154- Type : "service_instance" ,
155- })
174+ // Find COS instance of the bucket
175+ listServiceInstanceOptions := & resourcecontrollerv2.ListResourceInstancesOptions {
176+ ResourceID : ptr .To (utils .CosResourceID ),
177+ }
178+
179+ workspaces , _ , err := pvsClient .ResourceControllerClient .ListResourceInstances (listServiceInstanceOptions )
156180 if err != nil {
157- return err
181+ return fmt . Errorf ( "failed to list the resource instances: %v" , err )
158182 }
159- cosInstanceName , cosID , crn := findCOSInstanceDetails (svcs , bxCli )
160- if cosInstanceName == "" {
183+ if len (workspaces .Resources ) == 0 {
184+ return fmt .Errorf ("no service instances were found" )
185+ }
186+
187+ cosInstance := findCOSInstanceDetails (workspaces .Resources , pvsClient )
188+ if cosInstance == nil {
161189 return fmt .Errorf ("failed to find the COS instance for the bucket mentioned: %s" , opt .BucketName )
162190 }
163191
164- keys , err := bxCli .GetResourceKeys (cosID )
192+ klog .Infof ("Identified bucket %q in service instance: %s" , opt .BucketName , * cosInstance .Name )
193+ listResourceKeysInstanceOptions := & resourcecontrollerv2.ListResourceKeysForInstanceOptions {
194+ ID : cosInstance .GUID ,
195+ }
196+ keys , _ , err := pvsClient .ResourceControllerClient .ListResourceKeysForInstance (listResourceKeysInstanceOptions )
165197 if err != nil {
166- return fmt .Errorf ("failed to list the service credentials : %v" , err )
198+ return fmt .Errorf ("cannot list the resource keys for instance. err : %v" , err )
167199 }
168200
169- var cred map [string ]interface {}
170- var ok bool
171- if len (keys ) == 0 {
172- if opt .ServiceCredName == "" {
173- opt .ServiceCredName = serviceCredPrefix + "-" + cosInstanceName
174- }
201+ var ok , credentialsPresent bool
202+ var hmacKeys map [string ]interface {}
203+ var key * resourcecontrollerv2.ResourceKey
175204
176- // Create the service credential if does not exist
177- klog .V (2 ).Infof ("Auto Generating the COS Service credential for importing the image with name: %s" , opt .ServiceCredName )
178- CreateServiceKeyRequest := controller.CreateServiceKeyRequest {
179- Name : opt .ServiceCredName ,
180- SourceCRN : crn ,
181- Parameters : map [string ]interface {}{"HMAC" : true },
182- }
183- newKey , err := bxCli .ResourceServiceKey .CreateKey (CreateServiceKeyRequest )
184- if err != nil {
185- return err
205+ if opt .ServiceCredName == "" {
206+ opt .ServiceCredName = serviceCredPrefix + "-" + * cosInstance .Name
207+ }
208+
209+ // Create the service credential if does not exist
210+ if len (keys .Resources ) == 0 {
211+ if key , err = createNewCredentialsWithHMAC (pvsClient , * cosInstance .CRN , opt .ServiceCredName ); err != nil {
212+ return fmt .Errorf ("error while creating HMAC credentials. err: %v" , err )
186213 }
187- cred , ok = newKey .Credentials ["cos_hmac_keys" ].(map [string ]interface {})
188214 } else {
189- // Use the service credential already created
190215 klog .V (2 ).Info ("Reading the existing service credential" )
191- cred , ok = keys [0 ].Credentials ["cos_hmac_keys" ].(map [string ]interface {})
216+ // Use the service credential already created. There may be a possibility that multiple credentials exist, but the HMAC credentials may not be present.
217+ // In such case, manually re-create the credentials.
218+
219+ for _ , serviceCredential := range keys .Resources {
220+ key , _ , err = pvsClient .ResourceControllerClient .GetResourceKey (
221+ & resourcecontrollerv2.GetResourceKeyOptions {
222+ ID : serviceCredential .ID ,
223+ },
224+ )
225+ if err != nil {
226+ return fmt .Errorf ("an error occured while retriving the resource key. err: %v" , err )
227+ }
228+ // if the current credential has COS HMAC keys, reuse the same for importing the image
229+ if prop := key .Credentials .GetProperty (cosHmacKeys ); prop != nil {
230+ klog .Infof ("HMAC keys are available from the credential %q, re-using the same for image upload" , * key .Name )
231+ credentialsPresent = true
232+ break
233+ }
234+ klog .Infof ("No credentials found in the key %q." , * key .Name )
235+ }
236+ // if all the available service credentials do not have HMAC, create one with HMAC.
237+ if ! credentialsPresent {
238+ if key , err = createNewCredentialsWithHMAC (pvsClient , * cosInstance .CRN , opt .ServiceCredName ); err != nil {
239+ return fmt .Errorf ("error while creating HMAC credentials. err: %v" , err )
240+ }
241+ }
192242 }
193- if ! ok {
194- return fmt .Errorf ("failed to get the accessKey and secretKey from service credential" )
243+
244+ prop := key .Credentials .GetProperty (cosHmacKeys )
245+ if prop == nil {
246+ return fmt .Errorf ("unable to retrieve COS HMAC keys" )
247+ }
248+
249+ if hmacKeys , ok = prop .(map [string ]interface {}); ! ok {
250+ return fmt .Errorf ("type assertion for HMAC keys failed" )
195251 }
196- //Assign the Access Key and Secret Key for further operation
197- opt .AccessKey = cred [ "access_key_id" ].(string )
198- opt .SecretKey = cred [ "secret_access_key" ].(string )
252+ // Assign the Access Key and Secret Key for further operation
253+ opt .AccessKey = hmacKeys [ accessKeyId ].(string )
254+ opt .SecretKey = hmacKeys [ secretAccessKey ].(string )
199255 }
200256
201257 //By default Bucket Access is private
@@ -231,7 +287,7 @@ pvsadm image import -n upstream-core-lon04 -b <BUCKETNAME> --object rhel-83-1003
231287 }
232288
233289 var image * pmodels.ImageReference = & pmodels.ImageReference {}
234- klog .V ( 1 ). Info ("Retrieving image details" )
290+ klog .Info ("Retrieving image details" )
235291
236292 if image .ImageID == nil {
237293 image , err = pvmclient .ImgClient .GetImageByName (opt .ImageName )
0 commit comments