Make some steps exportable, add 'GetInstanceMetadata' method

This commit is contained in:
Gennady Lipenkov 2020-04-27 02:19:08 +03:00
parent 696750de32
commit 9489a46f32
8 changed files with 103 additions and 15 deletions

View File

@ -26,7 +26,7 @@ func (a *Artifact) Id() string {
} }
func (*Artifact) Files() []string { func (*Artifact) Files() []string {
return nil return []string{""}
} }
//revive:enable:var-naming //revive:enable:var-naming

View File

@ -54,11 +54,11 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
// Build the steps // Build the steps
steps := []multistep.Step{ steps := []multistep.Step{
&stepCreateSSHKey{ &StepCreateSSHKey{
Debug: b.config.PackerDebug, Debug: b.config.PackerDebug,
DebugKeyPath: fmt.Sprintf("yc_%s.pem", b.config.PackerBuildName), DebugKeyPath: fmt.Sprintf("yc_%s.pem", b.config.PackerBuildName),
}, },
&stepCreateInstance{ &StepCreateInstance{
Debug: b.config.PackerDebug, Debug: b.config.PackerDebug,
SerialLogFile: b.config.SerialLogFile, SerialLogFile: b.config.SerialLogFile,
}, },
@ -72,7 +72,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
&common.StepCleanupTempKeys{ &common.StepCleanupTempKeys{
Comm: &b.config.Communicator, Comm: &b.config.Communicator,
}, },
&stepTeardownInstance{}, &StepTeardownInstance{},
&stepCreateImage{}, &stepCreateImage{},
} }
@ -93,6 +93,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
artifact := &Artifact{ artifact := &Artifact{
image: image.(*compute.Image), image: image.(*compute.Image),
config: &b.config, config: &b.config,
driver: driver,
StateData: map[string]interface{}{"generated_data": state.Get("generated_data")}, StateData: map[string]interface{}{"generated_data": state.Get("generated_data")},
} }
return artifact, nil return artifact, nil

View File

@ -16,4 +16,5 @@ type Driver interface {
DeleteInstance(ctx context.Context, instanceID string) error DeleteInstance(ctx context.Context, instanceID string) error
DeleteSubnet(ctx context.Context, subnetID string) error DeleteSubnet(ctx context.Context, subnetID string) error
DeleteNetwork(ctx context.Context, networkID string) error DeleteNetwork(ctx context.Context, networkID string) error
GetInstanceMetadata(ctx context.Context, name string, key string) (string, error)
} }

View File

@ -232,5 +232,22 @@ func (d *driverYC) DeleteDisk(ctx context.Context, diskID string) error {
_, err = op.Response() _, err = op.Response()
return err return err
}
func (d *driverYC) GetInstanceMetadata(ctx context.Context, id string, key string) (string, error) {
instance, err := d.sdk.Compute().Instance().Get(ctx, &compute.GetInstanceRequest{
InstanceId: id,
View: compute.InstanceView_FULL,
})
if err != nil {
return "", err
}
for k, v := range instance.GetMetadata() {
if k == key {
return v, nil
}
}
return "", fmt.Errorf("Instance metadata key, %s, not found.", key)
} }

View File

@ -18,7 +18,7 @@ import (
const StandardImagesFolderID = "standard-images" const StandardImagesFolderID = "standard-images"
type stepCreateInstance struct { type StepCreateInstance struct {
Debug bool Debug bool
SerialLogFile string SerialLogFile string
} }
@ -106,7 +106,7 @@ func getImage(ctx context.Context, c *Config, d Driver) (*Image, error) {
return &Image{}, errors.New("neither source_image_name nor source_image_family defined in config") return &Image{}, errors.New("neither source_image_name nor source_image_family defined in config")
} }
func (s *stepCreateInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { func (s *StepCreateInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
sdk := state.Get("sdk").(*ycsdk.SDK) sdk := state.Get("sdk").(*ycsdk.SDK)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
config := state.Get("config").(*Config) config := state.Get("config").(*Config)
@ -265,7 +265,7 @@ runcmd:
return multistep.ActionContinue return multistep.ActionContinue
} }
func (s *stepCreateInstance) Cleanup(state multistep.StateBag) { func (s *StepCreateInstance) Cleanup(state multistep.StateBag) {
config := state.Get("config").(*Config) config := state.Get("config").(*Config)
driver := state.Get("driver").(Driver) driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
@ -339,7 +339,7 @@ func (s *stepCreateInstance) Cleanup(state multistep.StateBag) {
} }
} }
func (s *stepCreateInstance) writeSerialLogFile(ctx context.Context, state multistep.StateBag) error { func (s *StepCreateInstance) writeSerialLogFile(ctx context.Context, state multistep.StateBag) error {
sdk := state.Get("sdk").(*ycsdk.SDK) sdk := state.Get("sdk").(*ycsdk.SDK)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)

View File

@ -15,12 +15,12 @@ import (
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
) )
type stepCreateSSHKey struct { type StepCreateSSHKey struct {
Debug bool Debug bool
DebugKeyPath string DebugKeyPath string
} }
func (s *stepCreateSSHKey) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { func (s *StepCreateSSHKey) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
config := state.Get("config").(*Config) config := state.Get("config").(*Config)
@ -96,5 +96,5 @@ func (s *stepCreateSSHKey) Run(_ context.Context, state multistep.StateBag) mult
return multistep.ActionContinue return multistep.ActionContinue
} }
func (s *stepCreateSSHKey) Cleanup(state multistep.StateBag) { func (s *StepCreateSSHKey) Cleanup(state multistep.StateBag) {
} }

View File

@ -11,9 +11,9 @@ import (
ycsdk "github.com/yandex-cloud/go-sdk" ycsdk "github.com/yandex-cloud/go-sdk"
) )
type stepTeardownInstance struct{} type StepTeardownInstance struct{}
func (s *stepTeardownInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { func (s *StepTeardownInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
sdk := state.Get("sdk").(*ycsdk.SDK) sdk := state.Get("sdk").(*ycsdk.SDK)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
c := state.Get("config").(*Config) c := state.Get("config").(*Config)
@ -52,6 +52,6 @@ func (s *stepTeardownInstance) Run(ctx context.Context, state multistep.StateBag
return multistep.ActionContinue return multistep.ActionContinue
} }
func (s *stepTeardownInstance) Cleanup(state multistep.StateBag) { func (s *StepTeardownInstance) Cleanup(state multistep.StateBag) {
// no cleanup // no cleanup
} }

View File

@ -0,0 +1,69 @@
package yandex
import (
"context"
"errors"
"fmt"
"time"
"github.com/hashicorp/packer/common/retry"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
const CloudInitScriptStatusKey = "cloud-init-status"
const StartupScriptStatusError = "cloud-init-error"
const StartupScriptStatusDone = "cloud-init-done"
type StepWaitCloudInitScript int
// Run reads the instance metadata and looks for the log entry
// indicating the cloud-init script finished.
func (s *StepWaitCloudInitScript) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
_ = state.Get("config").(*Config)
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)
instanceID := state.Get("instance_id").(string)
ui.Say("Waiting for any running cloud-init script to finish...")
// Keep checking the serial port output to see if the cloud-init script is done.
err := retry.Config{
ShouldRetry: func(error) bool {
return true
},
RetryDelay: (&retry.Backoff{InitialBackoff: 10 * time.Second, MaxBackoff: 60 * time.Second, Multiplier: 2}).Linear,
}.Run(ctx, func(ctx context.Context) error {
status, err := driver.GetInstanceMetadata(ctx, instanceID, CloudInitScriptStatusKey)
if err != nil {
err := fmt.Errorf("Error getting cloud-init script status: %s", err)
return err
}
if status == StartupScriptStatusError {
err = errors.New("Cloud-init script error.")
return err
}
done := status == StartupScriptStatusDone
if !done {
ui.Say("Cloud-init script not finished yet. Waiting...")
return errors.New("Cloud-init script not done.")
}
return nil
})
if err != nil {
err := fmt.Errorf("Error waiting for cloud-init script to finish: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
ui.Say("Cloud-init script has finished running.")
return multistep.ActionContinue
}
// Cleanup.
func (s *StepWaitCloudInitScript) Cleanup(state multistep.StateBag) {}