@@ -15,7 +15,10 @@ limitations under the License.
1515package gcecloudprovider
1616
1717import (
18+ "context"
1819 "fmt"
20+ "golang.org/x/oauth2/google"
21+ "gopkg.in/gcfg.v1"
1922 "net/http"
2023 "os"
2124 "runtime"
@@ -24,9 +27,8 @@ import (
2427 "cloud.google.com/go/compute/metadata"
2528 "github.com/golang/glog"
2629 "golang.org/x/oauth2"
27- "golang.org/x/oauth2/google"
2830 beta "google.golang.org/api/compute/v0.beta"
29- compute "google.golang.org/api/compute/v1"
31+ "google.golang.org/api/compute/v1"
3032 "google.golang.org/api/googleapi"
3133 "k8s.io/apimachinery/pkg/util/wait"
3234)
@@ -57,20 +59,44 @@ type CloudProvider struct {
5759
5860var _ GCECompute = & CloudProvider {}
5961
60- func CreateCloudProvider (vendorVersion string ) (* CloudProvider , error ) {
61- svc , err := createCloudService (vendorVersion )
62+ type ConfigFile struct {
63+ Global ConfigGlobal `gcfg:"global"`
64+ }
65+
66+ type ConfigGlobal struct {
67+ TokenURL string `gcfg:"token-url"`
68+ TokenBody string `gcfg:"token-body"`
69+ ProjectId string `gcfg:"project-id"`
70+ }
71+
72+ func CreateCloudProvider (vendorVersion string , configPath string ) (* CloudProvider , error ) {
73+ configFile , err := readConfig (configPath )
74+ if err != nil {
75+ return nil , err
76+ }
77+ // At this point configFile could still be nil.
78+ // Any following code that uses configFile should handle nil pointer gracefully.
79+
80+ glog .V (1 ).Infof ("Using GCE provider config %+v" , configFile )
81+
82+ tokenSource , err := generateTokenSource (configFile )
83+ if err != nil {
84+ return nil , err
85+ }
86+
87+ svc , err := createCloudService (vendorVersion , tokenSource )
6288 if err != nil {
6389 return nil , err
6490 }
6591
66- betasvc , err := createBetaCloudService (vendorVersion )
92+ betasvc , err := createBetaCloudService (vendorVersion , tokenSource )
6793 if err != nil {
6894 return nil , err
6995 }
7096
71- project , zone , err := getProjectAndZoneFromMetadata ( )
97+ project , zone , err := getProjectAndZone ( configFile )
7298 if err != nil {
73- return nil , fmt .Errorf ("Failed getting Project and Zone from Metadata server : %v" , err )
99+ return nil , fmt .Errorf ("Failed getting Project and Zone: %v" , err )
74100 }
75101
76102 return & CloudProvider {
@@ -83,8 +109,55 @@ func CreateCloudProvider(vendorVersion string) (*CloudProvider, error) {
83109
84110}
85111
86- func createBetaCloudService (vendorVersion string ) (* beta.Service , error ) {
87- client , err := newDefaultOauthClient ()
112+ func generateTokenSource (configFile * ConfigFile ) (oauth2.TokenSource , error ) {
113+
114+ if configFile != nil && configFile .Global .TokenURL != "" && configFile .Global .TokenURL != "nil" {
115+ // configFile.Global.TokenURL is defined
116+ // Use AltTokenSource
117+
118+ tokenSource := NewAltTokenSource (configFile .Global .TokenURL , configFile .Global .TokenBody )
119+ glog .V (4 ).Infof ("Using AltTokenSource %#v" , tokenSource )
120+ return tokenSource , nil
121+ }
122+
123+ // Use DefaultTokenSource
124+
125+ tokenSource , err := google .DefaultTokenSource (
126+ context .Background (),
127+ compute .CloudPlatformScope ,
128+ compute .ComputeScope )
129+
130+ // DefaultTokenSource relies on GOOGLE_APPLICATION_CREDENTIALS env var being set.
131+ if gac , ok := os .LookupEnv ("GOOGLE_APPLICATION_CREDENTIALS" ); ok {
132+ glog .V (4 ).Infof ("GOOGLE_APPLICATION_CREDENTIALS env var set %v" , gac )
133+ } else {
134+ glog .Warningf ("GOOGLE_APPLICATION_CREDENTIALS env var not set" )
135+ }
136+ glog .V (4 ).Infof ("Using DefaultTokenSource %#v" , tokenSource )
137+
138+ return tokenSource , err
139+ }
140+
141+ func readConfig (configPath string ) (* ConfigFile , error ) {
142+ if configPath == "" {
143+ return nil , nil
144+ }
145+
146+ reader , err := os .Open (configPath )
147+ if err != nil {
148+ return nil , fmt .Errorf ("couldn't open cloud provider configuration at %s: %v" , configPath , err )
149+ }
150+ defer reader .Close ()
151+
152+ cfg := & ConfigFile {}
153+ if err := gcfg .FatalOnly (gcfg .ReadInto (cfg , reader )); err != nil {
154+ return nil , fmt .Errorf ("couldn't read cloud provider configuration at %s: %v" , configPath , err )
155+ }
156+ return cfg , nil
157+ }
158+
159+ func createBetaCloudService (vendorVersion string , tokenSource oauth2.TokenSource ) (* beta.Service , error ) {
160+ client , err := newOauthClient (tokenSource )
88161 if err != nil {
89162 return nil , err
90163 }
@@ -96,13 +169,13 @@ func createBetaCloudService(vendorVersion string) (*beta.Service, error) {
96169 return service , nil
97170}
98171
99- func createCloudService (vendorVersion string ) (* compute.Service , error ) {
100- svc , err := createCloudServiceWithDefaultServiceAccount (vendorVersion )
172+ func createCloudService (vendorVersion string , tokenSource oauth2. TokenSource ) (* compute.Service , error ) {
173+ svc , err := createCloudServiceWithDefaultServiceAccount (vendorVersion , tokenSource )
101174 return svc , err
102175}
103176
104- func createCloudServiceWithDefaultServiceAccount (vendorVersion string ) (* compute.Service , error ) {
105- client , err := newDefaultOauthClient ( )
177+ func createCloudServiceWithDefaultServiceAccount (vendorVersion string , tokenSource oauth2. TokenSource ) (* compute.Service , error ) {
178+ client , err := newOauthClient ( tokenSource )
106179 if err != nil {
107180 return nil , err
108181 }
@@ -114,22 +187,7 @@ func createCloudServiceWithDefaultServiceAccount(vendorVersion string) (*compute
114187 return service , nil
115188}
116189
117- func newDefaultOauthClient () (* http.Client , error ) {
118- // No compute token source, fallback on default
119- tokenSource , err := google .DefaultTokenSource (
120- oauth2 .NoContext ,
121- compute .CloudPlatformScope ,
122- compute .ComputeScope )
123- if gac , ok := os .LookupEnv ("GOOGLE_APPLICATION_CREDENTIALS" ); ok {
124- glog .V (4 ).Infof ("GOOGLE_APPLICATION_CREDENTIALS env var set %v" , gac )
125- } else {
126- glog .Warningf ("GOOGLE_APPLICATION_CREDENTIALS env var not set" )
127- }
128- glog .V (4 ).Infof ("Using DefaultTokenSource %#v" , tokenSource )
129- if err != nil {
130- return nil , err
131- }
132-
190+ func newOauthClient (tokenSource oauth2.TokenSource ) (* http.Client , error ) {
133191 if err := wait .PollImmediate (5 * time .Second , 30 * time .Second , func () (bool , error ) {
134192 if _ , err := tokenSource .Token (); err != nil {
135193 glog .Errorf ("error fetching initial token: %v" , err )
@@ -140,18 +198,32 @@ func newDefaultOauthClient() (*http.Client, error) {
140198 return nil , err
141199 }
142200
143- return oauth2 .NewClient (oauth2 . NoContext , tokenSource ), nil
201+ return oauth2 .NewClient (context . Background () , tokenSource ), nil
144202}
145203
146- func getProjectAndZoneFromMetadata () (string , string , error ) {
204+ func getProjectAndZone (config * ConfigFile ) (string , string , error ) {
205+ var err error
206+
147207 zone , err := metadata .Zone ()
148208 if err != nil {
149209 return "" , "" , err
150210 }
151- projectID , err := metadata .ProjectID ()
152- if err != nil {
153- return "" , "" , err
211+
212+ var projectID string
213+ if config == nil || config .Global .ProjectId == "" {
214+ // Project ID is not available from the local GCE cloud provider config file.
215+ // This could happen if the driver is not running in the master VM.
216+ // Defaulting to project ID from the Metadata server.
217+ projectID , err = metadata .ProjectID ()
218+ if err != nil {
219+ return "" , "" , err
220+ }
221+ glog .V (4 ).Infof ("Using GCP project ID from the Metadata server: %q" , projectID )
222+ } else {
223+ projectID = config .Global .ProjectId
224+ glog .V (4 ).Infof ("Using GCP project ID from the local GCE cloud provider config file: %q" , projectID )
154225 }
226+
155227 return projectID , zone , nil
156228}
157229
0 commit comments