github.com/StackPointCloud/packer@v0.10.2-0.20180716202532-b28098e0f79b/builder/oracle/oci/driver_oci.go (about)

     1  package oci
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"time"
     8  
     9  	core "github.com/oracle/oci-go-sdk/core"
    10  )
    11  
    12  // driverOCI implements the Driver interface and communicates with Oracle
    13  // OCI.
    14  type driverOCI struct {
    15  	computeClient core.ComputeClient
    16  	vcnClient     core.VirtualNetworkClient
    17  	cfg           *Config
    18  	context       context.Context
    19  }
    20  
    21  // NewDriverOCI Creates a new driverOCI with a connected compute client and a connected vcn client.
    22  func NewDriverOCI(cfg *Config) (Driver, error) {
    23  	coreClient, err := core.NewComputeClientWithConfigurationProvider(cfg.ConfigProvider)
    24  	if err != nil {
    25  		return nil, err
    26  	}
    27  
    28  	vcnClient, err := core.NewVirtualNetworkClientWithConfigurationProvider(cfg.ConfigProvider)
    29  	if err != nil {
    30  		return nil, err
    31  	}
    32  
    33  	return &driverOCI{
    34  		computeClient: coreClient,
    35  		vcnClient:     vcnClient,
    36  		cfg:           cfg,
    37  	}, nil
    38  }
    39  
    40  // CreateInstance creates a new compute instance.
    41  func (d *driverOCI) CreateInstance(ctx context.Context, publicKey string) (string, error) {
    42  	metadata := map[string]string{
    43  		"ssh_authorized_keys": publicKey,
    44  	}
    45  	if d.cfg.UserData != "" {
    46  		metadata["user_data"] = d.cfg.UserData
    47  	}
    48  
    49  	instanceDetails := core.LaunchInstanceDetails{
    50  		AvailabilityDomain: &d.cfg.AvailabilityDomain,
    51  		CompartmentId:      &d.cfg.CompartmentID,
    52  		ImageId:            &d.cfg.BaseImageID,
    53  		Shape:              &d.cfg.Shape,
    54  		SubnetId:           &d.cfg.SubnetID,
    55  		Metadata:           metadata,
    56  	}
    57  
    58  	// When empty, the default display name is used.
    59  	if d.cfg.InstanceName != "" {
    60  		instanceDetails.DisplayName = &d.cfg.InstanceName
    61  	}
    62  
    63  	instance, err := d.computeClient.LaunchInstance(context.TODO(), core.LaunchInstanceRequest{LaunchInstanceDetails: instanceDetails})
    64  
    65  	if err != nil {
    66  		return "", err
    67  	}
    68  
    69  	return *instance.Id, nil
    70  }
    71  
    72  // CreateImage creates a new custom image.
    73  func (d *driverOCI) CreateImage(ctx context.Context, id string) (core.Image, error) {
    74  	res, err := d.computeClient.CreateImage(ctx, core.CreateImageRequest{CreateImageDetails: core.CreateImageDetails{
    75  		CompartmentId: &d.cfg.CompartmentID,
    76  		InstanceId:    &id,
    77  		DisplayName:   &d.cfg.ImageName,
    78  	}})
    79  
    80  	if err != nil {
    81  		return core.Image{}, err
    82  	}
    83  
    84  	return res.Image, nil
    85  }
    86  
    87  // DeleteImage deletes a custom image.
    88  func (d *driverOCI) DeleteImage(ctx context.Context, id string) error {
    89  	_, err := d.computeClient.DeleteImage(ctx, core.DeleteImageRequest{ImageId: &id})
    90  	return err
    91  }
    92  
    93  // GetInstanceIP returns the public or private IP corresponding to the given instance id.
    94  func (d *driverOCI) GetInstanceIP(ctx context.Context, id string) (string, error) {
    95  	vnics, err := d.computeClient.ListVnicAttachments(ctx, core.ListVnicAttachmentsRequest{
    96  		InstanceId:    &id,
    97  		CompartmentId: &d.cfg.CompartmentID,
    98  	})
    99  	if err != nil {
   100  		return "", err
   101  	}
   102  
   103  	if len(vnics.Items) == 0 {
   104  		return "", errors.New("instance has zero VNICs")
   105  	}
   106  
   107  	vnic, err := d.vcnClient.GetVnic(ctx, core.GetVnicRequest{VnicId: vnics.Items[0].VnicId})
   108  	if err != nil {
   109  		return "", fmt.Errorf("Error getting VNIC details: %s", err)
   110  	}
   111  
   112  	if d.cfg.UsePrivateIP {
   113  		return *vnic.PrivateIp, nil
   114  	}
   115  
   116  	if vnic.PublicIp == nil {
   117  		return "", fmt.Errorf("Error getting VNIC Public Ip for: %s", id)
   118  	}
   119  
   120  	return *vnic.PublicIp, nil
   121  }
   122  
   123  func (d *driverOCI) GetInstanceInitialCredentials(ctx context.Context, id string) (string, string, error) {
   124  	credentials, err := d.computeClient.GetWindowsInstanceInitialCredentials(ctx, core.GetWindowsInstanceInitialCredentialsRequest{
   125  		InstanceId: &id,
   126  	})
   127  	if err != nil {
   128  		return "", "", err
   129  	}
   130  
   131  	return *credentials.InstanceCredentials.Username, *credentials.InstanceCredentials.Password, err
   132  }
   133  
   134  // TerminateInstance terminates a compute instance.
   135  func (d *driverOCI) TerminateInstance(ctx context.Context, id string) error {
   136  	_, err := d.computeClient.TerminateInstance(ctx, core.TerminateInstanceRequest{
   137  		InstanceId: &id,
   138  	})
   139  	return err
   140  }
   141  
   142  // WaitForImageCreation waits for a provisioning custom image to reach the
   143  // "AVAILABLE" state.
   144  func (d *driverOCI) WaitForImageCreation(ctx context.Context, id string) error {
   145  	return waitForResourceToReachState(
   146  		func(string) (string, error) {
   147  			image, err := d.computeClient.GetImage(ctx, core.GetImageRequest{ImageId: &id})
   148  			if err != nil {
   149  				return "", err
   150  			}
   151  			return string(image.LifecycleState), nil
   152  		},
   153  		id,
   154  		[]string{"PROVISIONING"},
   155  		"AVAILABLE",
   156  		0,             //Unlimited Retries
   157  		5*time.Second, //5 second wait between retries
   158  	)
   159  }
   160  
   161  // WaitForInstanceState waits for an instance to reach the a given terminal
   162  // state.
   163  func (d *driverOCI) WaitForInstanceState(ctx context.Context, id string, waitStates []string, terminalState string) error {
   164  	return waitForResourceToReachState(
   165  		func(string) (string, error) {
   166  			instance, err := d.computeClient.GetInstance(ctx, core.GetInstanceRequest{InstanceId: &id})
   167  			if err != nil {
   168  				return "", err
   169  			}
   170  			return string(instance.LifecycleState), nil
   171  		},
   172  		id,
   173  		waitStates,
   174  		terminalState,
   175  		0,             //Unlimited Retries
   176  		5*time.Second, //5 second wait between retries
   177  	)
   178  }
   179  
   180  // WaitForResourceToReachState checks the response of a request through a
   181  // polled get and waits until the desired state or until the max retried has
   182  // been reached.
   183  func waitForResourceToReachState(getResourceState func(string) (string, error), id string, waitStates []string, terminalState string, maxRetries int, waitDuration time.Duration) error {
   184  	for i := 0; maxRetries == 0 || i < maxRetries; i++ {
   185  		state, err := getResourceState(id)
   186  		if err != nil {
   187  			return err
   188  		}
   189  
   190  		if stringSliceContains(waitStates, state) {
   191  			time.Sleep(waitDuration)
   192  			continue
   193  		} else if state == terminalState {
   194  			return nil
   195  		}
   196  		return fmt.Errorf("Unexpected resource state %q, expecting a waiting state %s or terminal state  %q ", state, waitStates, terminalState)
   197  	}
   198  	return fmt.Errorf("Maximum number of retries (%d) exceeded; resource did not reach state %q", maxRetries, terminalState)
   199  }
   200  
   201  // stringSliceContains loops through a slice of strings returning a boolean
   202  // based on whether a given value is contained in the slice.
   203  func stringSliceContains(slice []string, value string) bool {
   204  	for _, elem := range slice {
   205  		if elem == value {
   206  			return true
   207  		}
   208  	}
   209  	return false
   210  }