github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/provider/equinix/instance.go (about)

     1  // Copyright 2020 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package equinix
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/packethost/packngo"
    11  
    12  	"github.com/juju/juju/core/instance"
    13  	"github.com/juju/juju/core/network"
    14  	corenetwork "github.com/juju/juju/core/network"
    15  	"github.com/juju/juju/core/network/firewall"
    16  	"github.com/juju/juju/core/status"
    17  	"github.com/juju/juju/environs/context"
    18  	"github.com/juju/juju/environs/instances"
    19  	"github.com/juju/juju/provider/common"
    20  )
    21  
    22  type equinixDevice struct {
    23  	e *environ
    24  
    25  	*packngo.Device
    26  	newInstanceConfigurator func(string) common.InstanceConfigurator
    27  }
    28  
    29  var (
    30  	_ instances.Instance           = (*equinixDevice)(nil)
    31  	_ instances.InstanceFirewaller = (*equinixDevice)(nil)
    32  )
    33  
    34  // newInstance returns a new equinixDevice
    35  func newInstance(raw *packngo.Device, env *environ) *equinixDevice {
    36  	return &equinixDevice{
    37  		Device:                  raw,
    38  		e:                       env,
    39  		newInstanceConfigurator: common.NewSshInstanceConfigurator,
    40  	}
    41  }
    42  
    43  func (device *equinixDevice) String() string {
    44  	return device.ID
    45  }
    46  
    47  func (device *equinixDevice) Id() instance.Id {
    48  	return instance.Id(device.ID)
    49  }
    50  
    51  func (device *equinixDevice) Status(ctx context.ProviderCallContext) instance.Status {
    52  	var jujuStatus status.Status
    53  
    54  	switch device.State {
    55  	case Active:
    56  		jujuStatus = status.Running
    57  	case Provisioning:
    58  		jujuStatus = status.Pending
    59  	case ShuttingDown, Stopped, Stopping, Terminated:
    60  		jujuStatus = status.Empty
    61  	default:
    62  		jujuStatus = status.Empty
    63  	}
    64  
    65  	return instance.Status{
    66  		Status:  jujuStatus,
    67  		Message: device.State,
    68  	}
    69  }
    70  
    71  // Addresses implements network.Addresses() returning generic address
    72  // details for the instance, and requerying the equinix api if required.
    73  func (device *equinixDevice) Addresses(ctx context.ProviderCallContext) (corenetwork.ProviderAddresses, error) {
    74  	var addresses []corenetwork.ProviderAddress
    75  
    76  	for _, netw := range device.Network {
    77  		address := corenetwork.ProviderAddress{}
    78  		address.Value = netw.Address
    79  		address.CIDR = fmt.Sprintf("%s/%d", netw.Network, netw.CIDR)
    80  
    81  		if netw.Public {
    82  			address.Scope = corenetwork.ScopePublic
    83  		} else {
    84  			address.Scope = corenetwork.ScopeCloudLocal
    85  		}
    86  
    87  		if netw.AddressFamily == 4 {
    88  			address.Type = network.IPv4Address
    89  		} else {
    90  			address.Type = network.IPv6Address
    91  			logger.Infof("skipping IPv6 Address %s", netw.Address)
    92  
    93  			continue
    94  		}
    95  
    96  		addresses = append(addresses, address)
    97  	}
    98  
    99  	return addresses, nil
   100  }
   101  
   102  // OpenPorts (InstanceFirewaller) ensures that the input ingress rule is
   103  // permitted for machine with the input ID.
   104  func (device *equinixDevice) OpenPorts(ctx context.ProviderCallContext, _ string, rules firewall.IngressRules) error {
   105  	client, err := device.getInstanceConfigurator(ctx)
   106  	if err != nil {
   107  		return errors.Trace(err)
   108  	}
   109  	return errors.Trace(client.ChangeIngressRules("", true, rules))
   110  }
   111  
   112  // OpenPorts (InstanceFirewaller) ensures that the input ingress rule is
   113  // restricted for machine with the input ID.
   114  func (device *equinixDevice) ClosePorts(ctx context.ProviderCallContext, _ string, rules firewall.IngressRules) error {
   115  	client, err := device.getInstanceConfigurator(ctx)
   116  	if err != nil {
   117  		return errors.Trace(err)
   118  	}
   119  	return errors.Trace(client.ChangeIngressRules("", false, rules))
   120  }
   121  
   122  // IngressRules (InstanceFirewaller) returns the ingress rules that have been
   123  // applied to the input machine ID.
   124  func (device *equinixDevice) IngressRules(ctx context.ProviderCallContext, _ string) (firewall.IngressRules, error) {
   125  	client, err := device.getInstanceConfigurator(ctx)
   126  	if err != nil {
   127  		return nil, errors.Trace(err)
   128  	}
   129  
   130  	rules, err := client.FindIngressRules()
   131  	return rules, errors.Trace(err)
   132  }
   133  
   134  func (device *equinixDevice) getInstanceConfigurator(
   135  	ctx context.ProviderCallContext,
   136  ) (common.InstanceConfigurator, error) {
   137  	addresses, err := device.Addresses(ctx)
   138  	if err != nil {
   139  		return nil, errors.Trace(err)
   140  	}
   141  
   142  	// Try to find a public address.
   143  	// Different models use different VCNs (and therefore subnets),
   144  	// so the cloud-local IPs are no good if a controller is trying to
   145  	// configure an instance in another model.
   146  	for _, addr := range addresses {
   147  		if addr.Scope == corenetwork.ScopePublic {
   148  			return device.newInstanceConfigurator(addr.Value), nil
   149  		}
   150  	}
   151  
   152  	return nil, errors.NotFoundf("public address for instance %q", device.Id())
   153  }