Merge pull request #9379 from GennadySpb/sa-auth-in-yandex-export

Support Authentication by Service Account Key file in Yandex Export post-processor
This commit is contained in:
Megan Marsh 2020-06-10 09:40:02 -07:00 committed by GitHub
commit a0a3ddbadb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 131 additions and 17 deletions

View File

@ -10,6 +10,8 @@ import (
"strings"
"time"
"github.com/yandex-cloud/go-sdk/iamkey"
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer/builder/yandex"
"github.com/hashicorp/packer/common"
@ -46,6 +48,10 @@ type Config struct {
// OAuth token to use to authenticate to Yandex.Cloud. Alternatively you may set
// value by environment variable YC_TOKEN.
Token string `mapstructure:"token" required:"false"`
// Path to file with Service Account key in json format. This
// is an alternative method to authenticate to Yandex.Cloud. Alternatively you may set environment variable
// YC_SERVICE_ACCOUNT_KEY_FILE.
ServiceAccountKeyFile string `mapstructure:"service_account_key_file" required:"false"`
ctx interpolate.Context
}
@ -78,6 +84,31 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
p.config.Token = os.Getenv("YC_TOKEN")
}
if p.config.ServiceAccountKeyFile == "" {
p.config.ServiceAccountKeyFile = os.Getenv("YC_SERVICE_ACCOUNT_KEY_FILE")
}
if p.config.Token == "" && p.config.ServiceAccountKeyFile == "" {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("a token or service account key file must be specified"))
}
if p.config.Token != "" && p.config.ServiceAccountKeyFile != "" {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("one of token or service account key file must be specified, not both"))
}
if p.config.Token != "" {
packer.LogSecretFilter.Set(p.config.Token)
}
if p.config.ServiceAccountKeyFile != "" {
if _, err := iamkey.ReadFromJSONFile(p.config.ServiceAccountKeyFile); err != nil {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("fail to read service account key file: %s", err))
}
}
if p.config.FolderID == "" {
p.config.FolderID = os.Getenv("YC_FOLDER_ID")
}
@ -133,6 +164,7 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, artifact
yandexConfig := ycSaneDefaults()
yandexConfig.Token = p.config.Token
yandexConfig.ServiceAccountKeyFile = p.config.ServiceAccountKeyFile
yandexConfig.DiskName = exporterName
yandexConfig.InstanceName = exporterName
yandexConfig.DiskSizeGb = p.config.DiskSizeGb
@ -144,8 +176,9 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, artifact
if p.config.ServiceAccountID != "" {
yandexConfig.ServiceAccountID = p.config.ServiceAccountID
}
if p.config.PlatformID != "" {
yandexConfig.ServiceAccountID = p.config.ServiceAccountID
yandexConfig.PlatformID = p.config.PlatformID
}
driver, err := yandex.NewDriverYC(ui, &yandexConfig)

View File

@ -9,22 +9,23 @@ import (
// FlatConfig is an auto-generated flat version of Config.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatConfig struct {
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
Paths []string `mapstructure:"paths" required:"true" cty:"paths" hcl:"paths"`
FolderID *string `mapstructure:"folder_id" required:"true" cty:"folder_id" hcl:"folder_id"`
ServiceAccountID *string `mapstructure:"service_account_id" required:"true" cty:"service_account_id" hcl:"service_account_id"`
DiskSizeGb *int `mapstructure:"disk_size" required:"false" cty:"disk_size" hcl:"disk_size"`
DiskType *string `mapstructure:"disk_type" required:"false" cty:"disk_type" hcl:"disk_type"`
PlatformID *string `mapstructure:"platform_id" required:"false" cty:"platform_id" hcl:"platform_id"`
SubnetID *string `mapstructure:"subnet_id" required:"false" cty:"subnet_id" hcl:"subnet_id"`
Zone *string `mapstructure:"zone" required:"false" cty:"zone" hcl:"zone"`
Token *string `mapstructure:"token" required:"false" cty:"token" hcl:"token"`
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
Paths []string `mapstructure:"paths" required:"true" cty:"paths" hcl:"paths"`
FolderID *string `mapstructure:"folder_id" required:"true" cty:"folder_id" hcl:"folder_id"`
ServiceAccountID *string `mapstructure:"service_account_id" required:"true" cty:"service_account_id" hcl:"service_account_id"`
DiskSizeGb *int `mapstructure:"disk_size" required:"false" cty:"disk_size" hcl:"disk_size"`
DiskType *string `mapstructure:"disk_type" required:"false" cty:"disk_type" hcl:"disk_type"`
PlatformID *string `mapstructure:"platform_id" required:"false" cty:"platform_id" hcl:"platform_id"`
SubnetID *string `mapstructure:"subnet_id" required:"false" cty:"subnet_id" hcl:"subnet_id"`
Zone *string `mapstructure:"zone" required:"false" cty:"zone" hcl:"zone"`
Token *string `mapstructure:"token" required:"false" cty:"token" hcl:"token"`
ServiceAccountKeyFile *string `mapstructure:"service_account_key_file" required:"false" cty:"service_account_key_file" hcl:"service_account_key_file"`
}
// FlatMapstructure returns a new FlatConfig.
@ -55,6 +56,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"subnet_id": &hcldec.AttrSpec{Name: "subnet_id", Type: cty.String, Required: false},
"zone": &hcldec.AttrSpec{Name: "zone", Type: cty.String, Required: false},
"token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false},
"service_account_key_file": &hcldec.AttrSpec{Name: "service_account_key_file", Type: cty.String, Required: false},
}
return s
}

View File

@ -0,0 +1,66 @@
package yandexexport
import (
"testing"
"github.com/hashicorp/packer/helper/multistep"
)
func TestPostProcessor_Configure(t *testing.T) {
type fields struct {
config Config
runner multistep.Runner
}
type args struct {
raws []interface{}
}
tests := []struct {
name string
fields fields
args args
wantErr bool
}{
{
name: "no one creds",
fields: fields{
config: Config{
Token: "",
ServiceAccountKeyFile: "",
},
},
wantErr: true,
},
{
name: "both token and sa key file",
fields: fields{
config: Config{
Token: "some-value",
ServiceAccountKeyFile: "path/not-exist.file",
},
},
wantErr: true,
},
{
name: "use sa key file",
fields: fields{
config: Config{
Token: "",
ServiceAccountKeyFile: "testdata/fake-sa-key.json",
},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.fields.config.Paths = []string{"some-path"} // make Paths not empty
p := &PostProcessor{
config: tt.fields.config,
runner: tt.fields.runner,
}
if err := p.Configure(tt.args.raws...); (err != nil) != tt.wantErr {
t.Errorf("Configure() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

View File

@ -0,0 +1,9 @@
{
"id": "ajeboa0du6edu6m43c3t",
"service_account_id": "ajeq7dsmihqple6761c5",
"created_at": "2018-11-19T13:38:09Z",
"description": "description",
"key_algorithm": "RSA_4096",
"public_key": "-----BEGIN PUBLIC KEY-----\nMIICCgKCAgEAo/s1lN5vFpFNJvS/l+yRilQHAPDeC3JqBwpLstbqJXW4kAUaKKoe\nxkIuJuPUKOUcd/JE3LXOEt/LOFb9mkCRdpjaIW7Jd5Fw0kTHIZ5rDoq7DZx0LV9b\nGJNskdccd6M6stb1GEqVuGpVcyXMCH8tMSG3c85DkcAg0cxXgyrirAzHMPiWSTpj\nJjICkxXRVj01Xq7dIDqL2LSMrZ2kLda5m+CnfscUbwnGRPPoEg20jLiEgBM2o43e\nhpWko1NStRR5fMQcQSUBbdtvbfPracjZz2/fq4fZfqlnObgq3WpYpdGynniLH3i5\nbxPM3ufYL3HY2w5aIOY6KIwMKLf3WYlug90ieviMYAvCukrCASwyqBQlt3MKCHlN\nIcebZXJDQ1VSBuEs+4qXYlhG1p+5C07zahzigNNTm6rEo47FFfClF04mv2uJN42F\nfWlEPR+V9JHBcfcBCdvyhiGzftl/vDo2NdO751ETIhyNKzxM/Ve2PR9h/qcuEatC\nLlXUA+40epNNHbSxAauxcngyrtkn7FZAEhdjyTtx46sELyb90Z56WgnbNUUGnsS/\nHBnBy5z8RyCmI5MjTC2NtplVqtAWkG+x59mU3GoCeuI8EaNtu2YPXhl1ovRkS4NB\n1G0F4c5FiJ27/E2MbNKlV5iw9ICcDforATYTeqiXbkkEKqIIiZYZWOsCAwEAAQ==\n-----END PUBLIC KEY-----\n",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIJKQIBAAKCAgEAo/s1lN5vFpFNJvS/l+yRilQHAPDeC3JqBwpLstbqJXW4kAUa\nKKoexkIuJuPUKOUcd/JE3LXOEt/LOFb9mkCRdpjaIW7Jd5Fw0kTHIZ5rDoq7DZx0\nLV9bGJNskdccd6M6stb1GEqVuGpVcyXMCH8tMSG3c85DkcAg0cxXgyrirAzHMPiW\nSTpjJjICkxXRVj01Xq7dIDqL2LSMrZ2kLda5m+CnfscUbwnGRPPoEg20jLiEgBM2\no43ehpWko1NStRR5fMQcQSUBbdtvbfPracjZz2/fq4fZfqlnObgq3WpYpdGynniL\nH3i5bxPM3ufYL3HY2w5aIOY6KIwMKLf3WYlug90ieviMYAvCukrCASwyqBQlt3MK\nCHlNIcebZXJDQ1VSBuEs+4qXYlhG1p+5C07zahzigNNTm6rEo47FFfClF04mv2uJ\nN42FfWlEPR+V9JHBcfcBCdvyhiGzftl/vDo2NdO751ETIhyNKzxM/Ve2PR9h/qcu\nEatCLlXUA+40epNNHbSxAauxcngyrtkn7FZAEhdjyTtx46sELyb90Z56WgnbNUUG\nnsS/HBnBy5z8RyCmI5MjTC2NtplVqtAWkG+x59mU3GoCeuI8EaNtu2YPXhl1ovRk\nS4NB1G0F4c5FiJ27/E2MbNKlV5iw9ICcDforATYTeqiXbkkEKqIIiZYZWOsCAwEA\nAQKCAgEAihT1L6CGhshf4VfjJfktLQBIzYAGWjlEEx2WVMgobtbMTWoedvOZ6nS8\nDD943d7ftBkr53aoSrhslcqazpNkaiuYMuLpf2fXSxhjXmnZ2Gr1zCZcpgBP40fw\n+nXbINswiHv98zCLFrljrwy63MTKtz6fDkM4HrlcaY3aezdXnG0+JnyNgKhL6VPf\nWx/aIPZ1xH8W8RabwCV4+JFwOLFBpoLsSBM3n7DpZhLE7r7ftEeEO5zyO5MxOL81\n3dpCIP1Wt7sj169jnrBTCpGFQJTC5Kxd+kDw4nmf1LjCT6RHdYo5ELyM2jl8XI6d\ny24LWxhQ9VUGjAGSI6aabodLH/hcOBB2wG1tnO+n5y85GnKKOJgxCxaj1yR/LAcT\nFvZgbDGwAMd7h7+fU46Yj5BILk6mRvBNL6Mk2VAlBzUatGduU+Xxha3JkGxIJY4G\np1qPLNiP7as90mXXMgNEtsP2zXtyi+9q7XBOBnfL3ftHWQmu7MKQCHIKcNRchFJ4\nS1LtndjXtNchzDhbXru2qsRiASmL9u4CgZn/lM3kDHs+v2JI+V8cPk5XZhoPrrpP\nZ0SPeoLZEJ5/TtlTWAXXqP6F24rziBqnEJgpNCkeBnQYx2Rs9OKVsrlDk8cf3KkL\nH8qQ/86HYz9cEtFnVKAYOV5GtQsJRyzipMy7R/cegdtWJ8ScuiECggEBANOT7lBX\nRYw+k53TRpkk7NlWuQogKKEQx4PEf8A6HQj3SseH8u+tt3HfTFJktzWs/9EQerLS\nJky9bSPxBvDq0Zfj+IPamiY+c2w5a9WbLxk8UHCaUHcSUeWoWQwmCZqzXeUNj9f5\nQOfF+ajsqhaXE68/HuIj+dgOOn/XYyqNkxlidXa9U3gUanuftwRSephsGcsaEGTe\nep2My4Jj3hPH/9Qoith0X18atRru6RanK63bDl0FqAU/1uUycQr+h0hEwQHWoRiq\nNVXI1uxfi5/2pxK0w1MOzZLitwEQ/veCv6CZwNPf1SW1U8j70SvKVR8Z7gGDIPjS\n8klW2Z9g6gxPQ1MCggEBAMZpBFa4mEnsmt+paEFCGUtoeBapjZF94PBtdxII/T5t\ne5z4Iz7RMl+ixLhNepQu+0t+v1iDVJgDJuUjCsSF69jEca7gzmsWhs9d+gDU5Knm\n18ChbQyeaDvmqINCs2t45pA/mVIQHbA8L8n/ToI5P63ZELDUFVzZo9kerZu1ALNB\nRoG0PhIHrGkZKwL8oE72nrZmWtfjROsZBhu7FqJ0i7va/6fgNMuMtBC/abOC7yVT\nir5XP+ZGF8XNyIZ3Ic0X8xc+XqagYsf+XobHGmbSct/ZaDP3g1z4B/7JZcbYjuTZ\nMJ3s5T+6l/qo0dfDuaVBJFJrnw8YfahX/Bn4OQ2TuQkCggEBALfhs5dDogA3Spg6\nTPtAalCh3IP+WxFQwfW1S8pHN4DZW7Z6YxsHgY2IIo7hVZFi35pVli3gEsVTRI2e\nJwgvLSWzTgNac+qVED+Y0C1/h7mI/+g9VX2HAIJ2g53ZWTOIfCxcUw3DZTOKjmbP\n+StU9hiy5SZpWfT6uMDu8xLCpHvFZI1kEi0koT78GlW5US8zlF8+Mc1YxnwzJ5QV\nM6dBhQhgi/t/eHvxfEECLrYvZ/jbj2otRk/5oczkv/ZsLCsVBiGQ5cXH+D6sJI6e\no3zNI3tQewmurd/hBmf4239FtUHhHwOFX3w8Uas1oB9M5Bn5sS7DRl67BzPSNaUc\n140HPl0CggEAX1+13TXoxog8vkzBt7TdUdlK+KHSUmCvEwObnAjEKxEXvZGt55FJ\n5JzqcSmVRcv7sgOgWRzwOg4x0S1yDJvPjiiH+SdJMkLm1KF4/pNXw7AagBdYwxsW\nQc0Trd0PQBcixa48tizXCJM16aSXCZQZXykbk9Su3C4mS8UqcNGmH4S+LrUErUgR\nAYg+m7XyHWMBUe6LtoEh7Nzfic76B2d8j/WqtPjaiAn/uJk6ZzcGW+v3op1wMvH4\nlXXg8XosvljH2qF5gCFSuo40xBbLQyfgXmg0Zd6Rv8velAQdr2MD9U/NxexNGsBI\nNA6YqF4GTECvBAuFrwz3wkdhAN7IFhWveQKCAQBdfdHB3D+m+b/hZoEIv0nPcgQf\ncCOPPNO/ufObjWed2jTL3RjoDT337Mp3mYkoP4GE9n6cl7mjlcrf7KQeRG8k35fv\n3nMoMOp21qj9J66UgGf1/RHsV/+ljcu87ggYDCVKd8uGzkspRIQIsD77He/TwZNa\nyWL4fa1EvRU6STwi7CZFfhWhMF3rBGAPshABoyJZh6Z14cioAKSR0Sl6XZ5dcB9B\naoJM8sISSlOqMIJyNnyMtdE55Ag+P7LyMe2grxlwVTv3h0o5mHSzWnjSHVYvN4q5\n6h5UUopLtyVMGCwOJz+zNT7zFqi4XIGU8a8Lg1iiKtfjgHB2X8ZWZuXBdrTj\n-----END PRIVATE KEY-----\n"
}

View File

@ -14,4 +14,8 @@
- `token` (string) - OAuth token to use to authenticate to Yandex.Cloud. Alternatively you may set
value by environment variable YC_TOKEN.
- `service_account_key_file` (string) - Path to file with Service Account key in json format. This
is an alternative method to authenticate to Yandex.Cloud. Alternatively you may set environment variable
YC_SERVICE_ACCOUNT_KEY_FILE.