Skip to content

Commit

Permalink
Merge pull request #71 from ionos-cloud/feat/nic-ips-and-dhcp
Browse files Browse the repository at this point in the history
Feat: nic ips and dhcp
  • Loading branch information
rmocanu-ionos committed Mar 15, 2023
2 parents a86204e + 9b297c7 commit 798a4a4
Show file tree
Hide file tree
Showing 5 changed files with 215 additions and 50 deletions.
2 changes: 1 addition & 1 deletion internal/utils/client_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type ClientService interface {
CreateLan(datacenterId, name string, public bool) (*ionoscloud.LanPost, error)
RemoveLan(datacenterId, lanId string) error

CreateNat(name, datacenterId string, publicIps []string, lansToGateways map[string][]string, subnet string) (*ionoscloud.NatGateway, error)
CreateNat(datacenterId, name string, publicIps []string, lansToGateways map[string][]string, subnet string) (*ionoscloud.NatGateway, error)
GetNat(datacenterId string, natId string) (*ionoscloud.NatGateway, error)
GetNats(datacenterId string) (*ionoscloud.NatGateways, error)
RemoveNat(datacenterId, natId string) error
Expand Down
8 changes: 4 additions & 4 deletions internal/utils/mocks/ClientService.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions internal/utils/nat.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ package utils

import (
"fmt"
"strconv"
"time"

"github.com/docker/machine/libmachine/log"
"github.com/ionos-cloud/docker-machine-driver/internal/pointer"
"github.com/ionos-cloud/docker-machine-driver/pkg/sdk_utils"
sdkgo "github.com/ionos-cloud/sdk-go/v6"
"golang.org/x/exp/maps"
"strconv"
"time"
)

func (c *Client) GetNats(datacenterId string) (*sdkgo.NatGateways, error) {
Expand Down Expand Up @@ -69,7 +70,7 @@ func (nrm *NatRuleMaker) OpenPorts(protocol string, start int32, end int32) *Nat
return nrm
}

func (c *Client) CreateNat(name, datacenterId string, publicIps []string, lansToGateways map[string][]string, subnet string) (*sdkgo.NatGateway, error) {
func (c *Client) CreateNat(datacenterId, name string, publicIps []string, lansToGateways map[string][]string, subnet string) (*sdkgo.NatGateway, error) {
var lans []sdkgo.NatGatewayLanProperties

err := c.createLansIfNotExist(datacenterId, maps.Keys(lansToGateways))
Expand Down
97 changes: 63 additions & 34 deletions ionoscloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ import (
"context"
"encoding/base64"
"fmt"
"github.com/ionos-cloud/docker-machine-driver/internal/pointer"
"github.com/ionos-cloud/docker-machine-driver/pkg/extflag"
"io/ioutil"
"net"
"strconv"
"strings"
"time"
"unicode"

"github.com/ionos-cloud/docker-machine-driver/internal/pointer"
"github.com/ionos-cloud/docker-machine-driver/pkg/extflag"

"github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/libmachine/log"
"github.com/docker/machine/libmachine/mcnflag"
Expand Down Expand Up @@ -42,6 +43,8 @@ const (
flagDatacenterId = "ionoscloud-datacenter-id"
flagDatacenterName = "ionoscloud-datacenter-name"
flagLanId = "ionoscloud-lan-id"
flagNicDhcp = "ionoscloud-nic-dhcp"
flagNicIps = "ionoscloud-nic-ips"
flagLanName = "ionoscloud-lan-name"
flagVolumeAvailabilityZone = "ionoscloud-volume-availability-zone"
flagUserData = "ionoscloud-user-data"
Expand Down Expand Up @@ -102,6 +105,8 @@ type Driver struct {
Image string
ImagePassword string
Size int
NicDhcp bool
NicIps []string
Location string
CpuFamily string
ServerType string
Expand Down Expand Up @@ -196,6 +201,16 @@ func (d *Driver) GetCreateFlags() []mcnflag.Flag {
EnvVar: extflag.KebabCaseToEnvVarCase(flagPrivateLan),
Usage: "Should the created LAN be private? Does nothing if LAN ID is provided",
},
mcnflag.BoolFlag{
Name: flagNicDhcp,
EnvVar: extflag.KebabCaseToEnvVarCase(flagNicDhcp),
Usage: "Should the created NIC have DHCP set to true or false? Defaults to true",
},
mcnflag.StringSliceFlag{
Name: flagNicIps,
EnvVar: extflag.KebabCaseToEnvVarCase(flagNicIps),
Usage: "Ionos Cloud NIC IPs",
},
mcnflag.StringFlag{
Name: flagEndpoint,
EnvVar: extflag.KebabCaseToEnvVarCase(flagEndpoint),
Expand All @@ -220,7 +235,7 @@ func (d *Driver) GetCreateFlags() []mcnflag.Flag {
mcnflag.IntFlag{
Name: flagServerCores,
EnvVar: extflag.KebabCaseToEnvVarCase(flagServerCores),
Value: 4,
Value: 2,
Usage: "Ionos Cloud Server Cores (2, 3, 4, 5, 6, etc.)",
},
mcnflag.IntFlag{
Expand Down Expand Up @@ -355,6 +370,8 @@ func (d *Driver) SetConfigFromFlags(opts drivers.DriverOptions) error {
d.DatacenterName = opts.String(flagDatacenterName)
d.LanId = opts.String(flagLanId)
d.LanName = opts.String(flagLanName)
d.NicDhcp = opts.Bool(flagNicDhcp)
d.NicIps = opts.StringSlice(flagNicIps)
d.VolumeAvailabilityZone = opts.String(flagVolumeAvailabilityZone)
d.ServerAvailabilityZone = opts.String(flagServerAvailabilityZone)
d.UserData = opts.String(flagUserData)
Expand Down Expand Up @@ -556,19 +573,6 @@ func (d *Driver) Create() (err error) {
log.Debugf("SSH Key generated in file: %v", d.publicSSHKeyPath())
}

givenB64Userdata, _ := base64.StdEncoding.DecodeString(d.UserDataB64)
if ud := getPropertyWithFallback(string(givenB64Userdata), d.UserData, ""); ud != "" {
// Provided B64 User Data has priority over UI provided User Data
d.UserData = ud
}

if d.SSHUser != "root" {
d.UserData, err = d.addSSHUserToYaml()
if err != nil {
return err
}
}

result, err := d.getImageId(d.Image)
if err != nil {
return fmt.Errorf("error getting image/alias %s: %w", d.Image, err)
Expand All @@ -578,6 +582,8 @@ func (d *Driver) Create() (err error) {
alias = result
}

// Creating Data Center if one was not provided

var dc *sdkgo.Datacenter
if d.DatacenterId == "" {
d.DCExists = false
Expand All @@ -600,6 +606,8 @@ func (d *Driver) Create() (err error) {
log.Debugf("Datacenter ID: %v", d.DatacenterId)
}

// Creating LAN if one was not provided

if d.LanId == "" {
lan, err := d.client().CreateLan(d.DatacenterId, d.LanName, !d.PrivateLan)
if err != nil {
Expand Down Expand Up @@ -638,9 +646,25 @@ func (d *Driver) Create() (err error) {
}
}

// Creating the server with the volume attached

// User Data for cloud init
givenB64Userdata, _ := base64.StdEncoding.DecodeString(d.UserDataB64)
if ud := getPropertyWithFallback(string(givenB64Userdata), d.UserData, ""); ud != "" {
// Provided B64 User Data has priority over UI provided User Data
d.UserData = ud
}

if d.SSHUser != "root" {
d.UserData, err = d.addSSHUserToYaml()
if err != nil {
return err
}
}
ud := base64.StdEncoding.EncodeToString([]byte(d.UserData))
log.Infof("Using user data: %s", ud)

// Volume
floatDiskSize := float32(d.DiskSize)
volumeProperties := sdkgo.VolumeProperties{
Type: &d.DiskType,
Expand Down Expand Up @@ -712,34 +736,40 @@ func (d *Driver) Create() (err error) {
d.VolumeId = *(*server.Entities.GetVolumes().Items)[0].GetId()
log.Debugf("Volume ID: %v", d.VolumeId)

l, _ := strconv.Atoi(d.LanId)
ips := &[]string{}
// Reserve IP if needed

if !isLanPrivate || d.CreateNat {
ipsToReserve := 1 // for NIC
if d.CreateNat && d.NatPublicIps == nil {
ipsToReserve += 1 // for NAT
}
log.Debugf("Reserving %d ips", ipsToReserve)
ipBlock, err := d.client().CreateIpBlock(int32(ipsToReserve), d.Location)
providedNicIps := len(d.NicIps) != 0
reservedIps := &[]string{}

if !isLanPrivate && !providedNicIps ||
d.CreateNat && d.NatPublicIps == nil {
ipBlock, err := d.client().CreateIpBlock(1, d.Location)
if err != nil {
return fmt.Errorf("error creating ipblock: %w", err)
}
if ipBlockId, ok := ipBlock.GetIdOk(); ok && ipBlockId != nil {
d.IpBlockId = *ipBlockId
log.Debugf("IpBlock ID: %v", d.IpBlockId)
}
ips, err = d.client().GetIpBlockIps(ipBlock)
reservedIps, err = d.client().GetIpBlockIps(ipBlock)
if err != nil {
return err
}
}

ipsForAttachedNic := ips
if d.PrivateLan {
// Create NIC
var ipsForAttachedNic *[]string

if providedNicIps {
ipsForAttachedNic = &d.NicIps // If IPs are provided use those
} else if isLanPrivate {
ipsForAttachedNic = nil // Let CloudAPI generate an IP, which we can later use for the subnet
} else {
ipsForAttachedNic = reservedIps // For public NICs we use the generated IPs
}
nic, err := d.client().CreateAttachNIC(d.DatacenterId, d.ServerId, d.MachineName, true, int32(l), ipsForAttachedNic)

lanId, _ := strconv.Atoi(d.LanId)
nic, err := d.client().CreateAttachNIC(d.DatacenterId, d.ServerId, d.MachineName, d.NicDhcp, int32(lanId), ipsForAttachedNic)
if err != nil {
// TODO: Duplicated
log.Warn(rollingBackNotice)
Expand All @@ -760,8 +790,7 @@ func (d *Driver) Create() (err error) {

nicIps := &[]string{}
if nicProp, ok := nic.GetPropertiesOk(); ok && nicProp != nil {
if nicIps, ok = nicProp.GetIpsOk(); ok && nicIps != nil {
}
nicIps = nicProp.GetIps()
}
if len(*nicIps) > 0 && !isLanPrivate {
d.IPAddress = (*nicIps)[0]
Expand All @@ -771,17 +800,17 @@ func (d *Driver) Create() (err error) {
// --- NAT ---
if d.CreateNat {
// TODO: Were CreateNat in a deeper scope, we wouldn't have the need of these variables (they are here to avoid function-wide side-effects)
natPublicIps := &[]string{(*ips)[1]}
natLansToGateways := &map[string][]string{"1": {"10.0.0.1"}} // User has to add this ip route to their cloud config if he doesn't set a custom gateway IP
natPublicIps := reservedIps
if d.NatPublicIps != nil {
natPublicIps = &d.NatPublicIps
}
natLansToGateways := &map[string][]string{"1": {"10.0.0.1"}} // User has to add this ip route to their cloud config if he doesn't set a custom gateway IP
if d.NatLansToGateways != nil {
natLansToGateways = &d.NatLansToGateways
}
subnet := net.ParseIP((*nicIps)[0]).Mask(net.CIDRMask(24, 32)).String() + "/24"
log.Infof("Provisioning NAT with subnet: %s", subnet)
nat, err := d.client().CreateNat(d.NatName, d.DatacenterId, *natPublicIps, *natLansToGateways, subnet)
nat, err := d.client().CreateNat(d.DatacenterId, d.NatName, *natPublicIps, *natLansToGateways, subnet)
if err != nil {
return err
}
Expand Down
Loading

0 comments on commit 798a4a4

Please sign in to comment.