Merge pull request #7835 from hashicorp/google_oauth
replace some bespoke google auth code with code from golang's oauth2 …
This commit is contained in:
commit
a87ce366b3
|
@ -1,52 +1,34 @@
|
||||||
package googlecompute
|
package googlecompute
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
|
"golang.org/x/oauth2/google"
|
||||||
|
"golang.org/x/oauth2/jwt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// accountFile represents the structure of the account file JSON file.
|
func ProcessAccountFile(text string) (*jwt.Config, error) {
|
||||||
type AccountFile struct {
|
|
||||||
PrivateKeyId string `json:"private_key_id"`
|
|
||||||
PrivateKey string `json:"private_key"`
|
|
||||||
ClientEmail string `json:"client_email"`
|
|
||||||
ClientId string `json:"client_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseJSON(result interface{}, text string) error {
|
|
||||||
r := strings.NewReader(text)
|
|
||||||
dec := json.NewDecoder(r)
|
|
||||||
return dec.Decode(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ProcessAccountFile(account_file *AccountFile, text string) error {
|
|
||||||
// Assume text is a JSON string
|
// Assume text is a JSON string
|
||||||
if err := parseJSON(account_file, text); err != nil {
|
conf, err := google.JWTConfigFromJSON([]byte(text), DriverScopes...)
|
||||||
|
if err != nil {
|
||||||
// If text was not JSON, assume it is a file path instead
|
// If text was not JSON, assume it is a file path instead
|
||||||
if _, err := os.Stat(text); os.IsNotExist(err) {
|
if _, err := os.Stat(text); os.IsNotExist(err) {
|
||||||
return fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
"account_file path does not exist: %s",
|
"account_file path does not exist: %s",
|
||||||
text)
|
text)
|
||||||
}
|
}
|
||||||
|
data, err := ioutil.ReadFile(text)
|
||||||
b, err := ioutil.ReadFile(text)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
"Error reading account_file from path '%s': %s",
|
"Error reading account_file from path '%s': %s",
|
||||||
text, err)
|
text, err)
|
||||||
}
|
}
|
||||||
|
conf, err = google.JWTConfigFromJSON(data, DriverScopes...)
|
||||||
contents := string(b)
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error parsing account_file: %s", err)
|
||||||
if err := parseJSON(account_file, contents); err != nil {
|
|
||||||
return fmt.Errorf(
|
|
||||||
"Error parsing account file '%s': %s",
|
|
||||||
contents, err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return conf, nil
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
||||||
// representing a GCE machine image.
|
// representing a GCE machine image.
|
||||||
func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
|
func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
|
||||||
driver, err := NewDriverGCE(
|
driver, err := NewDriverGCE(
|
||||||
ui, b.config.ProjectId, &b.config.Account)
|
ui, b.config.ProjectId, b.config.Account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/hashicorp/packer/helper/config"
|
"github.com/hashicorp/packer/helper/config"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
"github.com/hashicorp/packer/template/interpolate"
|
"github.com/hashicorp/packer/template/interpolate"
|
||||||
|
"golang.org/x/oauth2/jwt"
|
||||||
compute "google.golang.org/api/compute/v1"
|
compute "google.golang.org/api/compute/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -65,7 +66,7 @@ type Config struct {
|
||||||
MetadataFiles map[string]string `mapstructure:"metadata_files"`
|
MetadataFiles map[string]string `mapstructure:"metadata_files"`
|
||||||
Zone string `mapstructure:"zone"`
|
Zone string `mapstructure:"zone"`
|
||||||
|
|
||||||
Account AccountFile
|
Account *jwt.Config
|
||||||
stateTimeout time.Duration
|
stateTimeout time.Duration
|
||||||
imageAlreadyExists bool
|
imageAlreadyExists bool
|
||||||
ctx interpolate.Context
|
ctx interpolate.Context
|
||||||
|
@ -209,9 +210,11 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.AccountFile != "" {
|
if c.AccountFile != "" {
|
||||||
if err := ProcessAccountFile(&c.Account, c.AccountFile); err != nil {
|
cfg, err := ProcessAccountFile(c.AccountFile)
|
||||||
|
if err != nil {
|
||||||
errs = packer.MultiErrorAppend(errs, err)
|
errs = packer.MultiErrorAppend(errs, err)
|
||||||
}
|
}
|
||||||
|
c.Account = cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.OmitExternalIP && c.Address != "" {
|
if c.OmitExternalIP && c.Address != "" {
|
||||||
|
|
|
@ -500,6 +500,16 @@ func testMetadataFile(t *testing.T) string {
|
||||||
return tf.Name()
|
return tf.Name()
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is just some dummy data that doesn't actually work (it was revoked
|
// This is just some dummy data that doesn't actually work
|
||||||
// a long time ago).
|
const testAccountContent = `{
|
||||||
const testAccountContent = `{}`
|
"type": "service_account",
|
||||||
|
"project_id": "test-project-123456789",
|
||||||
|
"private_key_id": "bananaphone",
|
||||||
|
"private_key": "-----BEGIN PRIVATE KEY-----\nring_ring_ring_ring_ring_ring_ring_BANANAPHONE\n-----END PRIVATE KEY-----\n",
|
||||||
|
"client_email": "raffi-compute@developer.gserviceaccount.com",
|
||||||
|
"client_id": "1234567890",
|
||||||
|
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
||||||
|
"token_uri": "https://accounts.google.com/o/oauth2/token",
|
||||||
|
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
||||||
|
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/12345-compute%40developer.gserviceaccount.com"
|
||||||
|
}`
|
||||||
|
|
|
@ -35,24 +35,17 @@ type driverGCE struct {
|
||||||
|
|
||||||
var DriverScopes = []string{"https://www.googleapis.com/auth/compute", "https://www.googleapis.com/auth/devstorage.full_control"}
|
var DriverScopes = []string{"https://www.googleapis.com/auth/compute", "https://www.googleapis.com/auth/devstorage.full_control"}
|
||||||
|
|
||||||
func NewClientGCE(a *AccountFile) (*http.Client, error) {
|
func NewClientGCE(conf *jwt.Config) (*http.Client, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
var client *http.Client
|
var client *http.Client
|
||||||
|
|
||||||
// Auth with AccountFile first if provided
|
// Auth with AccountFile first if provided
|
||||||
if a.PrivateKey != "" {
|
if len(conf.PrivateKey) > 0 {
|
||||||
log.Printf("[INFO] Requesting Google token via AccountFile...")
|
log.Printf("[INFO] Requesting Google token via account_file...")
|
||||||
log.Printf("[INFO] -- Email: %s", a.ClientEmail)
|
log.Printf("[INFO] -- Email: %s", conf.Email)
|
||||||
log.Printf("[INFO] -- Scopes: %s", DriverScopes)
|
log.Printf("[INFO] -- Scopes: %s", DriverScopes)
|
||||||
log.Printf("[INFO] -- Private Key Length: %d", len(a.PrivateKey))
|
log.Printf("[INFO] -- Private Key Length: %d", len(conf.PrivateKey))
|
||||||
|
|
||||||
conf := jwt.Config{
|
|
||||||
Email: a.ClientEmail,
|
|
||||||
PrivateKey: []byte(a.PrivateKey),
|
|
||||||
Scopes: DriverScopes,
|
|
||||||
TokenURL: "https://accounts.google.com/o/oauth2/token",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initiate an http.Client. The following GET request will be
|
// Initiate an http.Client. The following GET request will be
|
||||||
// authorized and authenticated on the behalf of
|
// authorized and authenticated on the behalf of
|
||||||
|
@ -82,8 +75,8 @@ func NewClientGCE(a *AccountFile) (*http.Client, error) {
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDriverGCE(ui packer.Ui, p string, a *AccountFile) (Driver, error) {
|
func NewDriverGCE(ui packer.Ui, p string, conf *jwt.Config) (Driver, error) {
|
||||||
client, err := NewClientGCE(a)
|
client, err := NewClientGCE(conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,7 @@ func (s *StepCreateWindowsPassword) Run(ctx context.Context, state multistep.Sta
|
||||||
UserName: c.Comm.WinRMUser,
|
UserName: c.Comm.WinRMUser,
|
||||||
Modulus: base64.StdEncoding.EncodeToString(priv.N.Bytes()),
|
Modulus: base64.StdEncoding.EncodeToString(priv.N.Bytes()),
|
||||||
Exponent: base64.StdEncoding.EncodeToString(buf[1:]),
|
Exponent: base64.StdEncoding.EncodeToString(buf[1:]),
|
||||||
Email: c.Account.ClientEmail,
|
Email: c.Account.Email,
|
||||||
ExpireOn: time.Now().Add(time.Minute * 5),
|
ExpireOn: time.Now().Add(time.Minute * 5),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
"github.com/hashicorp/packer/template/interpolate"
|
"github.com/hashicorp/packer/template/interpolate"
|
||||||
|
"golang.org/x/oauth2/jwt"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
@ -26,7 +27,7 @@ type Config struct {
|
||||||
Subnetwork string `mapstructure:"subnetwork"`
|
Subnetwork string `mapstructure:"subnetwork"`
|
||||||
Zone string `mapstructure:"zone"`
|
Zone string `mapstructure:"zone"`
|
||||||
|
|
||||||
Account googlecompute.AccountFile
|
Account *jwt.Config
|
||||||
ctx interpolate.Context
|
ctx interpolate.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,16 +97,18 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, artifact
|
||||||
|
|
||||||
// Set up credentials for GCE driver.
|
// Set up credentials for GCE driver.
|
||||||
if builderAccountFile != "" {
|
if builderAccountFile != "" {
|
||||||
err := googlecompute.ProcessAccountFile(&p.config.Account, builderAccountFile)
|
cfg, err := googlecompute.ProcessAccountFile(builderAccountFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, false, err
|
return nil, false, false, err
|
||||||
}
|
}
|
||||||
|
p.config.Account = cfg
|
||||||
}
|
}
|
||||||
if p.config.AccountFile != "" {
|
if p.config.AccountFile != "" {
|
||||||
err := googlecompute.ProcessAccountFile(&p.config.Account, p.config.AccountFile)
|
cfg, err := googlecompute.ProcessAccountFile(p.config.AccountFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, false, err
|
return nil, false, false, err
|
||||||
}
|
}
|
||||||
|
p.config.Account = cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up exporter instance configuration.
|
// Set up exporter instance configuration.
|
||||||
|
@ -139,7 +142,7 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, artifact
|
||||||
}
|
}
|
||||||
exporterConfig.CalcTimeout()
|
exporterConfig.CalcTimeout()
|
||||||
|
|
||||||
driver, err := googlecompute.NewDriverGCE(ui, builderProjectId, &p.config.Account)
|
driver, err := googlecompute.NewDriverGCE(ui, builderProjectId, p.config.Account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, false, err
|
return nil, false, false, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/oauth2/jwt"
|
||||||
"google.golang.org/api/compute/v1"
|
"google.golang.org/api/compute/v1"
|
||||||
"google.golang.org/api/storage/v1"
|
"google.golang.org/api/storage/v1"
|
||||||
|
|
||||||
|
@ -34,7 +35,7 @@ type Config struct {
|
||||||
ImageName string `mapstructure:"image_name"`
|
ImageName string `mapstructure:"image_name"`
|
||||||
SkipClean bool `mapstructure:"skip_clean"`
|
SkipClean bool `mapstructure:"skip_clean"`
|
||||||
|
|
||||||
Account googlecompute.AccountFile
|
Account *jwt.Config
|
||||||
ctx interpolate.Context
|
ctx interpolate.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,9 +71,11 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.config.AccountFile != "" {
|
if p.config.AccountFile != "" {
|
||||||
if err := googlecompute.ProcessAccountFile(&p.config.Account, p.config.AccountFile); err != nil {
|
cfg, err := googlecompute.ProcessAccountFile(p.config.AccountFile)
|
||||||
|
if err != nil {
|
||||||
errs = packer.MultiErrorAppend(errs, err)
|
errs = packer.MultiErrorAppend(errs, err)
|
||||||
}
|
}
|
||||||
|
p.config.Account = cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
templates := map[string]*string{
|
templates := map[string]*string{
|
||||||
|
@ -95,7 +98,7 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, bool, error) {
|
func (p *PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, bool, error) {
|
||||||
client, err := googlecompute.NewClientGCE(&p.config.Account)
|
client, err := googlecompute.NewClientGCE(p.config.Account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, false, err
|
return nil, false, false, err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue