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