package common import ( "context" "fmt" "log" "github.com/antihax/optional" "github.com/hashicorp/packer/common" "github.com/outscale/osc-go/oapi" "github.com/outscale/osc-sdk-go/osc" ) type stateRefreshFunc func() (string, error) func waitForSecurityGroup(conn *oapi.Client, securityGroupID string) error { errCh := make(chan error, 1) go waitForState(errCh, "exists", securityGroupWaitFunc(conn, securityGroupID)) err := <-errCh return err } func waitUntilForVmRunning(conn *oapi.Client, vmID string) error { errCh := make(chan error, 1) go waitForState(errCh, "running", waitUntilVmStateFunc(conn, vmID)) err := <-errCh return err } func waitUntilForOscVmRunning(conn *osc.APIClient, vmID string) error { errCh := make(chan error, 1) go waitForState(errCh, "running", waitUntilOscVmStateFunc(conn, vmID)) err := <-errCh return err } func waitUntilVmDeleted(conn *oapi.Client, vmID string) error { errCh := make(chan error, 1) go waitForState(errCh, "terminated", waitUntilVmStateFunc(conn, vmID)) return <-errCh } func waitUntilOscVmDeleted(conn *osc.APIClient, vmID string) error { errCh := make(chan error, 1) go waitForState(errCh, "terminated", waitUntilOscVmStateFunc(conn, vmID)) return <-errCh } func waitUntilVmStopped(conn *oapi.Client, vmID string) error { errCh := make(chan error, 1) go waitForState(errCh, "stopped", waitUntilVmStateFunc(conn, vmID)) return <-errCh } func WaitUntilSnapshotCompleted(conn *oapi.Client, id string) error { errCh := make(chan error, 1) go waitForState(errCh, "completed", waitUntilSnapshotStateFunc(conn, id)) return <-errCh } func WaitUntilImageAvailable(conn *oapi.Client, imageID string) error { errCh := make(chan error, 1) go waitForState(errCh, "available", waitUntilImageStateFunc(conn, imageID)) return <-errCh } func WaitUntilVolumeAvailable(conn *oapi.Client, volumeID string) error { errCh := make(chan error, 1) go waitForState(errCh, "available", volumeWaitFunc(conn, volumeID)) return <-errCh } func WaitUntilVolumeIsLinked(conn *oapi.Client, volumeID string) error { errCh := make(chan error, 1) go waitForState(errCh, "attached", waitUntilVolumeLinkedStateFunc(conn, volumeID)) return <-errCh } func WaitUntilVolumeIsUnlinked(conn *oapi.Client, volumeID string) error { errCh := make(chan error, 1) go waitForState(errCh, "dettached", waitUntilVolumeUnLinkedStateFunc(conn, volumeID)) return <-errCh } func WaitUntilSnapshotDone(conn *oapi.Client, snapshotID string) error { errCh := make(chan error, 1) go waitForState(errCh, "completed", waitUntilSnapshotDoneStateFunc(conn, snapshotID)) return <-errCh } func waitForState(errCh chan<- error, target string, refresh stateRefreshFunc) error { err := common.Retry(2, 2, 0, func(_ uint) (bool, error) { state, err := refresh() if err != nil { return false, err } else if state == target { return true, nil } return false, nil }) errCh <- err return err } func waitUntilVmStateFunc(conn *oapi.Client, id string) stateRefreshFunc { return func() (string, error) { log.Printf("[Debug] Retrieving state for VM with id %s", id) resp, err := conn.POST_ReadVms(oapi.ReadVmsRequest{ Filters: oapi.FiltersVm{ VmIds: []string{id}, }, }) log.Printf("[Debug] Read Response %+v", resp.OK) if err != nil { return "", err } if resp.OK == nil { return "", fmt.Errorf("Vm with ID %s not Found", id) } if len(resp.OK.Vms) == 0 { return "pending", nil } return resp.OK.Vms[0].State, nil } } func waitUntilOscVmStateFunc(conn *osc.APIClient, id string) stateRefreshFunc { return func() (string, error) { log.Printf("[Debug] Retrieving state for VM with id %s", id) resp, _, err := conn.VmApi.ReadVms(context.Background(), &osc.ReadVmsOpts{ ReadVmsRequest: optional.NewInterface(osc.ReadVmsRequest{ Filters: osc.FiltersVm{ VmIds: []string{id}, }, }), }) log.Printf("[Debug] Read Response %+v", resp) if err != nil { return "", err } //TODO: check if needed // if resp == nil { // return "", fmt.Errorf("Vm with ID %s not Found", id) // } if len(resp.Vms) == 0 { return "pending", nil } return resp.Vms[0].State, nil } } func waitUntilVolumeLinkedStateFunc(conn *oapi.Client, id string) stateRefreshFunc { return func() (string, error) { log.Printf("[Debug] Check if volume with id %s exists", id) resp, err := conn.POST_ReadVolumes(oapi.ReadVolumesRequest{ Filters: oapi.FiltersVolume{ VolumeIds: []string{id}, }, }) if err != nil { return "", err } if resp.OK == nil { return "", fmt.Errorf("Vm with ID %s. Not Found", id) } log.Printf("[Debug] Read Response %+v", resp.OK) if len(resp.OK.Volumes) == 0 { return "pending", nil } if len(resp.OK.Volumes[0].LinkedVolumes) == 0 { return "pending", nil } return resp.OK.Volumes[0].LinkedVolumes[0].State, nil } } func waitUntilVolumeUnLinkedStateFunc(conn *oapi.Client, id string) stateRefreshFunc { return func() (string, error) { log.Printf("[Debug] Check if volume with id %s exists", id) resp, err := conn.POST_ReadVolumes(oapi.ReadVolumesRequest{ Filters: oapi.FiltersVolume{ VolumeIds: []string{id}, }, }) if err != nil { return "", err } if resp.OK == nil { return "", fmt.Errorf("Vm with ID %s. Not Found", id) } log.Printf("[Debug] Read Response %+v", resp.OK) if len(resp.OK.Volumes) == 0 { return "pending", nil } if len(resp.OK.Volumes[0].LinkedVolumes) == 0 { return "dettached", nil } return "failed", nil } } func waitUntilSnapshotStateFunc(conn *oapi.Client, id string) stateRefreshFunc { return func() (string, error) { log.Printf("[Debug] Check if Snapshot with id %s exists", id) resp, err := conn.POST_ReadSnapshots(oapi.ReadSnapshotsRequest{ Filters: oapi.FiltersSnapshot{ SnapshotIds: []string{id}, }, }) log.Printf("[Debug] Read Response %+v", resp.OK) if err != nil { return "", err } if resp.OK == nil { return "", fmt.Errorf("Vm with ID %s. Not Found", id) } if len(resp.OK.Snapshots) == 0 { return "pending", nil } return resp.OK.Snapshots[0].State, nil } } func waitUntilImageStateFunc(conn *oapi.Client, id string) stateRefreshFunc { return func() (string, error) { log.Printf("[Debug] Check if Image with id %s exists", id) resp, err := conn.POST_ReadImages(oapi.ReadImagesRequest{ Filters: oapi.FiltersImage{ ImageIds: []string{id}, }, }) log.Printf("[Debug] Read Response %+v", resp.OK) if err != nil { return "", err } if resp.OK == nil { return "", fmt.Errorf("Vm with ID %s. Not Found", id) } if len(resp.OK.Images) == 0 { return "pending", nil } if resp.OK.Images[0].State == "failed" { return resp.OK.Images[0].State, fmt.Errorf("Image (%s) creation is failed", id) } return resp.OK.Images[0].State, nil } } func securityGroupWaitFunc(conn *oapi.Client, id string) stateRefreshFunc { return func() (string, error) { log.Printf("[Debug] Check if SG with id %s exists", id) resp, err := conn.POST_ReadSecurityGroups(oapi.ReadSecurityGroupsRequest{ Filters: oapi.FiltersSecurityGroup{ SecurityGroupIds: []string{id}, }, }) log.Printf("[Debug] Read Response %+v", resp.OK) if err != nil { return "", err } if resp.OK == nil { return "", fmt.Errorf("Security Group with ID %s. Not Found", id) } if len(resp.OK.SecurityGroups) == 0 { return "waiting", nil } return "exists", nil } } func waitUntilSnapshotDoneStateFunc(conn *oapi.Client, id string) stateRefreshFunc { return func() (string, error) { log.Printf("[Debug] Check if Snapshot with id %s exists", id) resp, err := conn.POST_ReadSnapshots(oapi.ReadSnapshotsRequest{ Filters: oapi.FiltersSnapshot{ SnapshotIds: []string{id}, }, }) log.Printf("[Debug] Read Response %+v", resp.OK) if err != nil { return "", err } if resp.OK == nil { return "", fmt.Errorf("Snapshot with ID %s. Not Found", id) } if len(resp.OK.Snapshots) == 0 { return "", fmt.Errorf("Snapshot with ID %s. Not Found", id) } if resp.OK.Snapshots[0].State == "error" { return resp.OK.Snapshots[0].State, fmt.Errorf("Snapshot (%s) creation is failed", id) } return resp.OK.Snapshots[0].State, nil } } func volumeWaitFunc(conn *oapi.Client, id string) stateRefreshFunc { return func() (string, error) { log.Printf("[Debug] Check if SvolumeG with id %s exists", id) resp, err := conn.POST_ReadVolumes(oapi.ReadVolumesRequest{ Filters: oapi.FiltersVolume{ VolumeIds: []string{id}, }, }) log.Printf("[Debug] Read Response %+v", resp.OK) if err != nil { return "", err } if resp.OK == nil { return "", fmt.Errorf("Volume with ID %s. Not Found", id) } if len(resp.OK.Volumes) == 0 { return "waiting", nil } if resp.OK.Volumes[0].State == "error" { return resp.OK.Volumes[0].State, fmt.Errorf("Volume (%s) creation is failed", id) } return resp.OK.Volumes[0].State, nil } }