66package ibm
77
88import (
9+ "context"
10+ "encoding/json"
911 "fmt"
1012 "os"
1113 "os/exec"
@@ -23,6 +25,7 @@ import (
2325 "github.com/cockroachdb/cockroach/pkg/roachprod/logger"
2426 "github.com/cockroachdb/cockroach/pkg/roachprod/vm"
2527 "github.com/cockroachdb/cockroach/pkg/roachprod/vm/flagstub"
28+ "github.com/cockroachdb/cockroach/pkg/util/ctxgroup"
2629 "github.com/cockroachdb/cockroach/pkg/util/randutil"
2730 "github.com/cockroachdb/cockroach/pkg/util/syncutil"
2831 "github.com/cockroachdb/cockroach/pkg/util/timeutil"
@@ -216,13 +219,29 @@ func NewProvider(options ...Option) (p *Provider, err error) {
216219
217220 // If an authenticator is not provided, create one from the environment.
218221 if p .authenticator == nil {
222+ // GetAuthenticatorFromEnvironment returns nil if IBM_APIKEY isn't set
223+ // 1) First tries to find a credential file
224+ // a) using IBM_CREDENTIALS_FILE which is not set
225+ // b) <user-home-dir>/ibm-credentials.env which is not currently used
226+ // c) <current-working-directory>/ibm-credentials.env which is not currently used
227+ // 2) Then uses the passed in string argument as a prefix and matches to
228+ // any env var with a matching prefix. If IBM_APIKEY isn't set then
229+ // this function continues.
230+ // 3) Finally, will try to load VCAP_SERVICES which is not currently used
231+ //
232+ // If all these fail to match, GetAuthenticatorFromEnvironment returns nil
233+ // without an error. We need to fail in that case, otherwise we will
234+ // continue on with a nil p.authenticator which will cause failures later
219235 p .authenticator , err = core .GetAuthenticatorFromEnvironment (defaultAPIKeyEnvVarPrefix )
220236 if err != nil {
221237 return nil , errors .Wrapf (
222238 err ,
223239 "failed to create default IBM authenticator from environment variables" ,
224240 )
225241 }
242+ if p .authenticator == nil {
243+ return nil , errors .Newf ("failed to create IBM authenticator. Is %s set?" , expectedEnvVarIBMAPIKey )
244+ }
226245 }
227246
228247 // Build the list of supported regions if none are provided.
@@ -864,6 +883,70 @@ func (p *Provider) FindActiveAccount(l *logger.Logger) (string, error) {
864883 return username , nil
865884}
866885
886+ // GetVMSpecs implements the vm.GetVMSpecs interface method which returns a
887+ // map from VM.Name to a map of VM attributes
888+ func (p * Provider ) GetVMSpecs (
889+ l * logger.Logger , vms vm.List ,
890+ ) (map [string ]map [string ]interface {}, error ) {
891+ // TODO replace background context with passed in context after *WithContext
892+ // interface methods are implemented for vm.Provider
893+ return p .GetVMSpecsWithContext (context .Background (), l , vms )
894+ }
895+
896+ func (p * Provider ) GetVMSpecsWithContext (
897+ ctx context.Context , l * logger.Logger , vms vm.List ,
898+ ) (map [string ]map [string ]interface {}, error ) {
899+
900+ // Extract the spec of all VMs and create a map from VM name to spec.
901+ vmSpecs := make (map [string ]map [string ]interface {})
902+ var mu syncutil.Mutex
903+ g := ctxgroup .WithContext (ctx )
904+ g .SetLimit (5 )
905+
906+ for _ , vmInstance := range vms {
907+ g .GoCtx (func (ctx context.Context ) error {
908+ perCtx , cancel := context .WithTimeout (ctx , time .Minute )
909+ defer cancel ()
910+
911+ region , err := p .zoneToRegion (vmInstance .Zone )
912+ if err != nil {
913+ return err
914+ }
915+ vpcService , err := p .getVpcService (region )
916+ if err != nil {
917+ return err
918+ }
919+ parsedCrn , err := p .parseCRN (vmInstance .ProviderID )
920+ if err != nil {
921+ return err
922+ }
923+ l .Printf ("Fetching specs for instance %s" , vmInstance .Name )
924+ result , _ , err := vpcService .GetInstanceWithContext (perCtx , vpcService .NewGetInstanceOptions (parsedCrn .id ))
925+ if err != nil {
926+ return err
927+ }
928+ // Marshaling & unmarshalling struct to match interface method return type
929+ jsonBytes , err := json .Marshal (result )
930+ if err != nil {
931+ return err
932+ }
933+ var vmSpec map [string ]interface {}
934+ err = json .Unmarshal (jsonBytes , & vmSpec )
935+ if err != nil {
936+ return err
937+ }
938+ mu .Lock ()
939+ vmSpecs [vmInstance .Name ] = vmSpec
940+ mu .Unlock ()
941+ return nil
942+ })
943+ }
944+ if err := g .Wait (); err != nil {
945+ return nil , err
946+ }
947+ return vmSpecs , nil
948+ }
949+
867950//
868951// Unimplemented methods, no plans to implement as of now.
869952//
@@ -885,13 +968,6 @@ func (p *Provider) GetLiveMigrationVMs(
885968 return nil , nil
886969}
887970
888- // GetVMSpecs is part of the vm.Provider interface.
889- func (p * Provider ) GetVMSpecs (
890- _ * logger.Logger , _ vm.List ,
891- ) (map [string ]map [string ]interface {}, error ) {
892- return nil , nil
893- }
894-
895971// Grow is part of the vm.Provider interface.
896972func (p * Provider ) Grow (_ * logger.Logger , _ vm.List , _ string , _ []string ) (vm.List , error ) {
897973 return nil , vm .UnimplementedError
0 commit comments