packer-cn/builder/oracle/oci/config_test.go

409 lines
9.7 KiB
Go

package oci
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"io/ioutil"
"os"
"strings"
"testing"
"github.com/go-ini/ini"
)
func testConfig(accessConfFile *os.File) map[string]interface{} {
return map[string]interface{}{
"availability_domain": "aaaa:PHX-AD-3",
"access_cfg_file": accessConfFile.Name(),
// Image
"base_image_ocid": "ocd1...",
"image_name": "HelloWorld",
// Networking
"subnet_ocid": "ocd1...",
// Comm
"ssh_username": "opc",
"use_private_ip": false,
"metadata": map[string]string{
"key": "value",
},
"defined_tags": map[string]map[string]interface{}{
"namespace": {"key": "value"},
},
// Instance Details
"instance_name": "hello-world",
"instance_tags": map[string]string{
"key": "value",
},
"create_vnic_details": map[string]interface{}{
"nsg_ids": []string{"ocd1..."},
},
"shape": "VM.Standard1.1",
"disk_size": 60,
}
}
func TestConfig(t *testing.T) {
// Shared set-up and deferred deletion
cfg, keyFile, err := baseTestConfigWithTmpKeyFile()
if err != nil {
t.Fatal(err)
}
defer os.Remove(keyFile.Name())
cfgFile, err := writeTestConfig(cfg)
if err != nil {
t.Fatal(err)
}
defer os.Remove(cfgFile.Name())
// Temporarily set $HOME to temp directory to bypass default
// access config loading.
tmpHome, err := ioutil.TempDir("", "packer_config_test")
if err != nil {
t.Fatalf("Unexpected error when creating temporary directory: %+v", err)
}
defer os.Remove(tmpHome)
home := os.Getenv("HOME")
os.Setenv("HOME", tmpHome)
defer os.Setenv("HOME", home)
// Config tests
t.Run("BaseConfig", func(t *testing.T) {
raw := testConfig(cfgFile)
var c Config
errs := c.Prepare(raw)
if errs != nil {
t.Fatalf("Unexpected error in configuration %+v", errs)
}
})
t.Run("BaseImageFilterWithoutOCID", func(t *testing.T) {
raw := testConfig(cfgFile)
raw["base_image_ocid"] = ""
raw["base_image_filter"] = map[string]interface{}{
"display_name": "hello_world",
}
var c Config
errs := c.Prepare(raw)
if errs != nil {
t.Fatalf("Unexpected error in configuration %+v", errs)
}
})
t.Run("BaseImageFilterDefault", func(t *testing.T) {
raw := testConfig(cfgFile)
var c Config
errs := c.Prepare(raw)
if errs != nil {
t.Fatalf("Unexpected error in configuration %+v", errs)
}
if *c.BaseImageFilter.Shape != raw["shape"] {
t.Fatalf("Default base_image_filter shape %v does not equal config shape %v",
*c.BaseImageFilter.Shape, raw["shape"])
}
})
t.Run("LaunchMode", func(t *testing.T) {
raw := testConfig(cfgFile)
raw["image_launch_mode"] = "NATIVE"
var c Config
errs := c.Prepare(raw)
if errs != nil {
t.Fatalf("Unexpected error in configuration %+v", errs)
}
})
t.Run("NoAccessConfig", func(t *testing.T) {
raw := testConfig(cfgFile)
delete(raw, "access_cfg_file")
var c Config
errs := c.Prepare(raw)
expectedErrors := []string{
"'user_ocid'", "'tenancy_ocid'", "'fingerprint'", "'key_file'",
}
s := errs.Error()
for _, expected := range expectedErrors {
if !strings.Contains(s, expected) {
t.Errorf("Expected %q to contain '%s'", s, expected)
}
}
})
t.Run("AccessConfigTemplateOnly", func(t *testing.T) {
raw := testConfig(cfgFile)
delete(raw, "access_cfg_file")
raw["user_ocid"] = "ocid1..."
raw["tenancy_ocid"] = "ocid1..."
raw["fingerprint"] = "00:00..."
raw["key_file"] = keyFile.Name()
var c Config
errs := c.Prepare(raw)
if errs != nil {
t.Fatalf("err: %+v", errs)
}
})
t.Run("TenancyReadFromAccessCfgFile", func(t *testing.T) {
raw := testConfig(cfgFile)
var c Config
errs := c.Prepare(raw)
if errs != nil {
t.Fatalf("Unexpected error in configuration %+v", errs)
}
tenancy, err := c.configProvider.TenancyOCID()
if err != nil {
t.Fatalf("Unexpected error getting tenancy ocid: %v", err)
}
expected := "ocid1.tenancy.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
if tenancy != expected {
t.Errorf("Expected tenancy: %s, got %s.", expected, tenancy)
}
})
t.Run("RegionNotDefaultedToPHXWhenSetInOCISettings", func(t *testing.T) {
raw := testConfig(cfgFile)
var c Config
errs := c.Prepare(raw)
if errs != nil {
t.Fatalf("Unexpected error in configuration %+v", errs)
}
region, err := c.configProvider.Region()
if err != nil {
t.Fatalf("Unexpected error getting region: %v", err)
}
expected := "us-ashburn-1"
if region != expected {
t.Errorf("Expected region: %s, got %s.", expected, region)
}
})
// Test the correct errors are produced when required template keys are
// omitted.
requiredKeys := []string{"availability_domain", "base_image_ocid", "shape", "subnet_ocid"}
for _, k := range requiredKeys {
t.Run(k+"_required", func(t *testing.T) {
raw := testConfig(cfgFile)
delete(raw, k)
var c Config
errs := c.Prepare(raw)
if !strings.Contains(errs.Error(), k) {
t.Errorf("Expected '%s' to contain '%s'", errs.Error(), k)
}
})
}
t.Run("ImageNameDefaultedIfEmpty", func(t *testing.T) {
raw := testConfig(cfgFile)
delete(raw, "image_name")
var c Config
errs := c.Prepare(raw)
if errs != nil {
t.Fatalf("Unexpected error in configuration %+v", errs)
}
if !strings.Contains(c.ImageName, "packer-") {
t.Errorf("got default ImageName %q, want image name 'packer-{{timestamp}}'", c.ImageName)
}
})
t.Run("user_ocid_overridden", func(t *testing.T) {
expected := "override"
raw := testConfig(cfgFile)
raw["user_ocid"] = expected
var c Config
errs := c.Prepare(raw)
if errs != nil {
t.Fatalf("Unexpected error in configuration %+v", errs)
}
user, _ := c.configProvider.UserOCID()
if user != expected {
t.Errorf("Expected ConfigProvider.UserOCID: %s, got %s", expected, user)
}
})
t.Run("tenancy_ocid_overidden", func(t *testing.T) {
expected := "override"
raw := testConfig(cfgFile)
raw["tenancy_ocid"] = expected
var c Config
errs := c.Prepare(raw)
if errs != nil {
t.Fatalf("Unexpected error in configuration %+v", errs)
}
tenancy, _ := c.configProvider.TenancyOCID()
if tenancy != expected {
t.Errorf("Expected ConfigProvider.TenancyOCID: %s, got %s", expected, tenancy)
}
})
t.Run("region_overidden", func(t *testing.T) {
expected := "override"
raw := testConfig(cfgFile)
raw["region"] = expected
var c Config
errs := c.Prepare(raw)
if errs != nil {
t.Fatalf("Unexpected error in configuration %+v", errs)
}
region, _ := c.configProvider.Region()
if region != expected {
t.Errorf("Expected ConfigProvider.Region: %s, got %s", expected, region)
}
})
t.Run("fingerprint_overidden", func(t *testing.T) {
expected := "override"
raw := testConfig(cfgFile)
raw["fingerprint"] = expected
var c Config
errs := c.Prepare(raw)
if errs != nil {
t.Fatalf("Unexpected error in configuration: %+v", errs)
}
fingerprint, _ := c.configProvider.KeyFingerprint()
if fingerprint != expected {
t.Errorf("Expected ConfigProvider.KeyFingerprint: %s, got %s", expected, fingerprint)
}
})
// Test the correct errors are produced when certain template keys
// are present alongside use_instance_principals key.
invalidKeys := []string{
"access_cfg_file",
"access_cfg_file_account",
"user_ocid",
"tenancy_ocid",
"region",
"fingerprint",
"key_file",
"pass_phrase",
}
for _, k := range invalidKeys {
t.Run(k+"_mixed_with_use_instance_principals", func(t *testing.T) {
raw := testConfig(cfgFile)
raw["use_instance_principals"] = "true"
raw[k] = "some_random_value"
var c Config
c.configProvider = instancePrincipalConfigurationProviderMock{}
errs := c.Prepare(raw)
if !strings.Contains(errs.Error(), k) {
t.Errorf("Expected '%s' to contain '%s'", errs.Error(), k)
}
})
}
}
// BaseTestConfig creates the base (DEFAULT) config including a temporary key
// file.
// NOTE: Caller is responsible for removing temporary key file.
func baseTestConfigWithTmpKeyFile() (*ini.File, *os.File, error) {
keyFile, err := generateRSAKeyFile()
if err != nil {
return nil, keyFile, err
}
// Build ini
cfg := ini.Empty()
section, _ := cfg.NewSection("DEFAULT")
section.NewKey("region", "us-ashburn-1")
section.NewKey("tenancy", "ocid1.tenancy.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
section.NewKey("user", "ocid1.user.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
section.NewKey("fingerprint", "70:04:5z:b3:19:ab:90:75:a4:1f:50:d4:c7:c3:33:20")
section.NewKey("key_file", keyFile.Name())
return cfg, keyFile, nil
}
// WriteTestConfig writes a ini.File to a temporary file for use in unit tests.
// NOTE: Caller is responsible for removing temporary file.
func writeTestConfig(cfg *ini.File) (*os.File, error) {
confFile, err := ioutil.TempFile("", "config_file")
if err != nil {
return nil, err
}
if _, err := confFile.Write([]byte("[DEFAULT]\n")); err != nil {
os.Remove(confFile.Name())
return nil, err
}
if _, err := cfg.WriteTo(confFile); err != nil {
os.Remove(confFile.Name())
return nil, err
}
return confFile, nil
}
// generateRSAKeyFile generates an RSA key file for use in unit tests.
// NOTE: The caller is responsible for deleting the temporary file.
func generateRSAKeyFile() (*os.File, error) {
// Create temporary file for the key
f, err := ioutil.TempFile("", "key")
if err != nil {
return nil, err
}
// Generate key
priv, err := rsa.GenerateKey(rand.Reader, 2014)
if err != nil {
return nil, err
}
// ASN.1 DER encoded form
privDer := x509.MarshalPKCS1PrivateKey(priv)
privBlk := pem.Block{
Type: "RSA PRIVATE KEY",
Headers: nil,
Bytes: privDer,
}
// Write the key out
if _, err := f.Write(pem.EncodeToMemory(&privBlk)); err != nil {
return nil, err
}
return f, nil
}