builder/googlecompute: use new auth scheme
This commit is contained in:
parent
13a0c2b09d
commit
b5c0c63ebc
|
@ -0,0 +1,35 @@
|
|||
package googlecompute
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
)
|
||||
|
||||
// accountFile represents the structure of the account file JSON file.
|
||||
type accountFile struct {
|
||||
PrivateKeyId string `json:"private_key_id"`
|
||||
PrivateKey string `json:"private_key"`
|
||||
ClientEmail string `json:"client_email"`
|
||||
ClientId string `json:"client_id"`
|
||||
}
|
||||
|
||||
// clientSecretsFile represents the structure of the client secrets JSON file.
|
||||
type clientSecretsFile struct {
|
||||
Web struct {
|
||||
AuthURI string `json:"auth_uri"`
|
||||
ClientEmail string `json:"client_email"`
|
||||
ClientId string `json:"client_id"`
|
||||
TokenURI string `json:"token_uri"`
|
||||
}
|
||||
}
|
||||
|
||||
func loadJSON(result interface{}, path string) error {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
dec := json.NewDecoder(f)
|
||||
return dec.Decode(result)
|
||||
}
|
|
@ -35,7 +35,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
// representing a GCE machine image.
|
||||
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
|
||||
driver, err := NewDriverGCE(
|
||||
ui, b.config.ProjectId, b.config.clientSecrets, b.config.privateKeyBytes)
|
||||
ui, b.config.ProjectId, &b.config.account, &b.config.clientSecrets)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
package googlecompute
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// clientSecrets represents the client secrets of a GCE service account.
|
||||
type clientSecrets struct {
|
||||
Web struct {
|
||||
AuthURI string `json:"auth_uri"`
|
||||
ClientEmail string `json:"client_email"`
|
||||
ClientId string `json:"client_id"`
|
||||
TokenURI string `json:"token_uri"`
|
||||
}
|
||||
}
|
||||
|
||||
// loadClientSecrets loads the GCE client secrets file identified by path.
|
||||
func loadClientSecrets(path string) (*clientSecrets, error) {
|
||||
var cs *clientSecrets
|
||||
secretBytes, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(secretBytes, &cs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cs, nil
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package googlecompute
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func testClientSecretsFile(t *testing.T) string {
|
||||
tf, err := ioutil.TempFile("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer tf.Close()
|
||||
|
||||
if _, err := tf.Write([]byte(testClientSecretsContent)); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
return tf.Name()
|
||||
}
|
||||
|
||||
func TestLoadClientSecrets(t *testing.T) {
|
||||
_, err := loadClientSecrets(testClientSecretsFile(t))
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// This is just some dummy data that doesn't actually work (it was revoked
|
||||
// a long time ago).
|
||||
const testClientSecretsContent = `{"web":{"auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://accounts.google.com/o/oauth2/token","client_email":"774313886706-eorlsj0r4eqkh5e7nvea5fuf59ifr873@developer.gserviceaccount.com","client_x509_cert_url":"https://www.googleapis.com/robot/v1/metadata/x509/774313886706-eorlsj0r4eqkh5e7nvea5fuf59ifr873@developer.gserviceaccount.com","client_id":"774313886706-eorlsj0r4eqkh5e7nvea5fuf59ifr873.apps.googleusercontent.com","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs"}}`
|
|
@ -16,8 +16,11 @@ import (
|
|||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
|
||||
AccountFile string `mapstructure:"account_file"`
|
||||
ClientSecretsFile string `mapstructure:"client_secrets_file"`
|
||||
ProjectId string `mapstructure:"project_id"`
|
||||
|
||||
BucketName string `mapstructure:"bucket_name"`
|
||||
ClientSecretsFile string `mapstructure:"client_secrets_file"`
|
||||
DiskSizeGb int64 `mapstructure:"disk_size"`
|
||||
ImageName string `mapstructure:"image_name"`
|
||||
ImageDescription string `mapstructure:"image_description"`
|
||||
|
@ -25,9 +28,6 @@ type Config struct {
|
|||
MachineType string `mapstructure:"machine_type"`
|
||||
Metadata map[string]string `mapstructure:"metadata"`
|
||||
Network string `mapstructure:"network"`
|
||||
Passphrase string `mapstructure:"passphrase"`
|
||||
PrivateKeyFile string `mapstructure:"private_key_file"`
|
||||
ProjectId string `mapstructure:"project_id"`
|
||||
SourceImage string `mapstructure:"source_image"`
|
||||
SourceImageProjectId string `mapstructure:"source_image_project_id"`
|
||||
SSHUsername string `mapstructure:"ssh_username"`
|
||||
|
@ -37,7 +37,8 @@ type Config struct {
|
|||
Tags []string `mapstructure:"tags"`
|
||||
Zone string `mapstructure:"zone"`
|
||||
|
||||
clientSecrets *clientSecrets
|
||||
account accountFile
|
||||
clientSecrets clientSecretsFile
|
||||
instanceName string
|
||||
privateKeyBytes []byte
|
||||
sshTimeout time.Duration
|
||||
|
@ -104,15 +105,15 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
|||
|
||||
// Process Templates
|
||||
templates := map[string]*string{
|
||||
"account_file": &c.AccountFile,
|
||||
"client_secrets_file": &c.ClientSecretsFile,
|
||||
|
||||
"bucket_name": &c.BucketName,
|
||||
"client_secrets_file": &c.ClientSecretsFile,
|
||||
"image_name": &c.ImageName,
|
||||
"image_description": &c.ImageDescription,
|
||||
"instance_name": &c.InstanceName,
|
||||
"machine_type": &c.MachineType,
|
||||
"network": &c.Network,
|
||||
"passphrase": &c.Passphrase,
|
||||
"private_key_file": &c.PrivateKeyFile,
|
||||
"project_id": &c.ProjectId,
|
||||
"source_image": &c.SourceImage,
|
||||
"source_image_project_id": &c.SourceImageProjectId,
|
||||
|
@ -137,16 +138,16 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
|||
errs, errors.New("a bucket_name must be specified"))
|
||||
}
|
||||
|
||||
if c.AccountFile == "" {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("an account_file must be specified"))
|
||||
}
|
||||
|
||||
if c.ClientSecretsFile == "" {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("a client_secrets_file must be specified"))
|
||||
}
|
||||
|
||||
if c.PrivateKeyFile == "" {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("a private_key_file must be specified"))
|
||||
}
|
||||
|
||||
if c.ProjectId == "" {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("a project_id must be specified"))
|
||||
|
@ -177,22 +178,17 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
|||
}
|
||||
c.stateTimeout = stateTimeout
|
||||
|
||||
if c.ClientSecretsFile != "" {
|
||||
// Load the client secrets file.
|
||||
cs, err := loadClientSecrets(c.ClientSecretsFile)
|
||||
if err != nil {
|
||||
if c.AccountFile != "" {
|
||||
if err := loadJSON(&c.account, c.AccountFile); err != nil {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, fmt.Errorf("Failed parsing client secrets file: %s", err))
|
||||
errs, fmt.Errorf("Failed parsing account file: %s", err))
|
||||
}
|
||||
c.clientSecrets = cs
|
||||
}
|
||||
|
||||
if c.PrivateKeyFile != "" {
|
||||
// Load the private key.
|
||||
c.privateKeyBytes, err = processPrivateKeyFile(c.PrivateKeyFile, c.Passphrase)
|
||||
if err != nil {
|
||||
if c.ClientSecretsFile != "" {
|
||||
if err := loadJSON(&c.clientSecrets, c.ClientSecretsFile); err != nil {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, fmt.Errorf("Failed loading private key file: %s", err))
|
||||
errs, fmt.Errorf("Failed parsing client secrets file: %s", err))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
package googlecompute
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func testConfig(t *testing.T) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"account_file": testAccountFile(t),
|
||||
"bucket_name": "foo",
|
||||
"client_secrets_file": testClientSecretsFile(t),
|
||||
"private_key_file": testPrivateKeyFile(t),
|
||||
"project_id": "hashicorp",
|
||||
"source_image": "foo",
|
||||
"zone": "us-east-1a",
|
||||
|
@ -84,16 +85,6 @@ func TestConfigPrepare(t *testing.T) {
|
|||
true,
|
||||
},
|
||||
|
||||
{
|
||||
"private_key_file",
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"private_key_file",
|
||||
testPrivateKeyFile(t),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"private_key_file",
|
||||
"/tmp/i/should/not/exist",
|
||||
|
@ -174,3 +165,37 @@ func TestConfigPrepare(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testAccountFile(t *testing.T) string {
|
||||
tf, err := ioutil.TempFile("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer tf.Close()
|
||||
|
||||
if _, err := tf.Write([]byte(testAccountContent)); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
return tf.Name()
|
||||
}
|
||||
|
||||
func testClientSecretsFile(t *testing.T) string {
|
||||
tf, err := ioutil.TempFile("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer tf.Close()
|
||||
|
||||
if _, err := tf.Write([]byte(testClientSecretsContent)); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
return tf.Name()
|
||||
}
|
||||
|
||||
// This is just some dummy data that doesn't actually work (it was revoked
|
||||
// a long time ago).
|
||||
const testAccountContent = `{}`
|
||||
|
||||
const testClientSecretsContent = `{"web":{"auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://accounts.google.com/o/oauth2/token","client_email":"774313886706-eorlsj0r4eqkh5e7nvea5fuf59ifr873@developer.gserviceaccount.com","client_x509_cert_url":"https://www.googleapis.com/robot/v1/metadata/x509/774313886706-eorlsj0r4eqkh5e7nvea5fuf59ifr873@developer.gserviceaccount.com","client_id":"774313886706-eorlsj0r4eqkh5e7nvea5fuf59ifr873.apps.googleusercontent.com","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs"}}`
|
||||
|
|
|
@ -23,22 +23,27 @@ type driverGCE struct {
|
|||
const DriverScopes string = "https://www.googleapis.com/auth/compute " +
|
||||
"https://www.googleapis.com/auth/devstorage.full_control"
|
||||
|
||||
func NewDriverGCE(ui packer.Ui, projectId string, c *clientSecrets, key []byte) (Driver, error) {
|
||||
log.Printf("[INFO] Requesting token...")
|
||||
log.Printf("[INFO] -- Email: %s", c.Web.ClientEmail)
|
||||
func NewDriverGCE(ui packer.Ui, p string, a *accountFile, c *clientSecretsFile) (Driver, error) {
|
||||
// Get the token for use in our requests
|
||||
log.Printf("[INFO] Requesting Google token...")
|
||||
log.Printf("[INFO] -- Email: %s", a.ClientEmail)
|
||||
log.Printf("[INFO] -- Scopes: %s", DriverScopes)
|
||||
log.Printf("[INFO] -- Private Key Length: %d", len(key))
|
||||
log.Printf("[INFO] -- Private Key Length: %d", len(a.PrivateKey))
|
||||
log.Printf("[INFO] -- Token URL: %s", c.Web.TokenURI)
|
||||
jwtTok := jwt.NewToken(c.Web.ClientEmail, DriverScopes, key)
|
||||
jwtTok := jwt.NewToken(
|
||||
a.ClientEmail,
|
||||
DriverScopes,
|
||||
[]byte(a.PrivateKey))
|
||||
jwtTok.ClaimSet.Aud = c.Web.TokenURI
|
||||
token, err := jwtTok.Assert(new(http.Client))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("Error retrieving auth token: %s", err)
|
||||
}
|
||||
|
||||
// Instantiate the transport to communicate to Google
|
||||
transport := &oauth.Transport{
|
||||
Config: &oauth.Config{
|
||||
ClientId: c.Web.ClientId,
|
||||
ClientId: a.ClientId,
|
||||
Scope: DriverScopes,
|
||||
TokenURL: c.Web.TokenURI,
|
||||
AuthURL: c.Web.AuthURI,
|
||||
|
@ -46,14 +51,14 @@ func NewDriverGCE(ui packer.Ui, projectId string, c *clientSecrets, key []byte)
|
|||
Token: token,
|
||||
}
|
||||
|
||||
log.Printf("[INFO] Instantiating client...")
|
||||
log.Printf("[INFO] Instantiating GCE client...")
|
||||
service, err := compute.New(transport.Client())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &driverGCE{
|
||||
projectId: projectId,
|
||||
projectId: p,
|
||||
service: service,
|
||||
ui: ui,
|
||||
}, nil
|
||||
|
|
Loading…
Reference in New Issue