packer-cn/builder/tencentcloud/cvm/step_config_key_pair.go
ZhiQiang Fan 1c7b23357d fix: correctly remove tencentcloud temporary keypair
Tencent Cloud key pair cannot be removed if it is in a image, so
when user doesn't specify any log in method, such as ssh_password,
temporary key pair will be created and used, which eventually will
always fail.

This patch detach temporary key pair before creating image, so in
cleanup step, it can be deleted correctly.

Note that if user specifies a private key pair, we do not detach it,
because user might want to use it when creating new instances from this
image.
2019-06-23 23:23:48 +08:00

131 lines
3.8 KiB
Go

package cvm
import (
"context"
"fmt"
"io/ioutil"
"os"
"runtime"
"time"
"github.com/hashicorp/packer/common/retry"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
)
type stepConfigKeyPair struct {
Debug bool
Comm *communicator.Config
DebugKeyPath string
keyID string
}
func (s *stepConfigKeyPair) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
if s.Comm.SSHPrivateKeyFile != "" {
ui.Say("Using existing SSH private key")
privateKeyBytes, err := ioutil.ReadFile(s.Comm.SSHPrivateKeyFile)
if err != nil {
state.Put("error", fmt.Errorf(
"loading configured private key file failed: %s", err))
return multistep.ActionHalt
}
s.Comm.SSHPrivateKey = privateKeyBytes
return multistep.ActionContinue
}
if s.Comm.SSHAgentAuth && s.Comm.SSHKeyPairName == "" {
ui.Say("Using SSH Agent with key pair in source image")
return multistep.ActionContinue
}
if s.Comm.SSHAgentAuth && s.Comm.SSHKeyPairName != "" {
ui.Say(fmt.Sprintf("Using SSH Agent for existing key pair %s", s.Comm.SSHKeyPairName))
return multistep.ActionContinue
}
if s.Comm.SSHTemporaryKeyPairName == "" {
ui.Say("Not using temporary keypair")
s.Comm.SSHKeyPairName = ""
return multistep.ActionContinue
}
client := state.Get("cvm_client").(*cvm.Client)
ui.Say(fmt.Sprintf("Creating temporary keypair: %s", s.Comm.SSHTemporaryKeyPairName))
req := cvm.NewCreateKeyPairRequest()
req.KeyName = &s.Comm.SSHTemporaryKeyPairName
defaultProjectId := int64(0)
req.ProjectId = &defaultProjectId
resp, err := client.CreateKeyPair(req)
if err != nil {
state.Put("error", fmt.Errorf("creating temporary keypair failed: %s", err.Error()))
return multistep.ActionHalt
}
// set keyId to delete when Cleanup
s.keyID = *resp.Response.KeyPair.KeyId
state.Put("temporary_key_pair_id", resp.Response.KeyPair.KeyId)
s.Comm.SSHKeyPairName = *resp.Response.KeyPair.KeyId
s.Comm.SSHPrivateKey = []byte(*resp.Response.KeyPair.PrivateKey)
if s.Debug {
ui.Message(fmt.Sprintf("Saving key for debug purposes: %s", s.DebugKeyPath))
f, err := os.Create(s.DebugKeyPath)
if err != nil {
state.Put("error", fmt.Errorf("creating debug key file failed:%s", err.Error()))
return multistep.ActionHalt
}
defer f.Close()
if _, err := f.Write([]byte(*resp.Response.KeyPair.PrivateKey)); err != nil {
state.Put("error", fmt.Errorf("writing debug key file failed:%s", err.Error()))
return multistep.ActionHalt
}
if runtime.GOOS != "windows" {
if err := f.Chmod(0600); err != nil {
state.Put("error", fmt.Errorf("setting debug key file's permission failed:%s", err.Error()))
return multistep.ActionHalt
}
}
}
return multistep.ActionContinue
}
func (s *stepConfigKeyPair) Cleanup(state multistep.StateBag) {
if s.Comm.SSHPrivateKeyFile != "" || (s.Comm.SSHKeyPairName == "" && s.keyID == "") {
return
}
ctx := context.TODO()
client := state.Get("cvm_client").(*cvm.Client)
ui := state.Get("ui").(packer.Ui)
ui.Say("Deleting temporary keypair...")
req := cvm.NewDeleteKeyPairsRequest()
req.KeyIds = []*string{&s.keyID}
err := retry.Config{
Tries: 60,
RetryDelay: (&retry.Backoff{InitialBackoff: 5 * time.Second, MaxBackoff: 5 * time.Second, Multiplier: 2}).Linear,
}.Run(ctx, func(ctx context.Context) error {
_, err := client.DeleteKeyPairs(req)
return err
})
if err != nil {
ui.Error(fmt.Sprintf(
"delete keypair failed, please delete it manually, keyId: %s, err: %s", s.keyID, err.Error()))
}
if s.Debug {
if err := os.Remove(s.DebugKeyPath); err != nil {
ui.Error(fmt.Sprintf("delete debug key file %s failed: %s", s.DebugKeyPath, err.Error()))
}
}
}