Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/multiple lans #102

Merged
merged 6 commits into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/rancher/rancher-cluster.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ You will create a Docker Container with the following command:
sudo docker run -d --restart=unless-stopped -p 80:80 -p 443:443 --privileged rancher/rancher
```

> **_NOTE:_** Please note that versions 2.8+ do not currently allow adding the IonosCloud UI extension for RKE2

To use a specific Rancher version, check the [available docker images](https://hub.docker.com/r/rancher/rancher/tags) and add the corresponding tag to the command:

```text
sudo docker run -d --restart=unless-stopped -p 80:80 -p 443:443 --privileged rancher/rancher:v2.5.x
sudo docker run -d --restart=unless-stopped -p 80:80 -p 443:443 --privileged rancher/rancher:v2.7.5
```

To output the available docker containers, use:
Expand Down
34 changes: 18 additions & 16 deletions docs/usage/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ docker run -d --name=rancher-server --restart=unless-stopped -p 80:80 -p 443:443

> **_NOTE:_** Note that version 2.7+ is required for Rancher UI Extension support

> **_NOTE:_** Please note that versions 2.8+ do not currently allow adding the IonosCloud UI extension for RKE2

### Configure Rancher (for RKE2)
* Rancher URL

Expand Down Expand Up @@ -72,22 +74,22 @@ docker exec -it <CONTAINER_ID> bash
Create a YAML file containing the driver info
```
echo "apiVersion: management.cattle.io/v3
kind: NodeDriver
metadata:
annotations:
lifecycle.cattle.io/create.node-driver-controller: "true"
privateCredentialFields: "token,username,password,endpoint"
name: ionoscloud
spec:
active: false
addCloudCredential: false
builtin: false
checksum: ""
description: ""
displayName: ionoscloud
externalId: ""
uiUrl: ""
url: https://github.com/ionos-cloud/docker-machine-driver/releases/download/v6.1.4/docker-machine-driver-6.1.4-linux-amd64.tar.gz" > ionos.yaml
kind: NodeDriver
metadata:
annotations:
lifecycle.cattle.io/create.node-driver-controller: "true"
privateCredentialFields: "token,username,password,endpoint"
name: ionoscloud
spec:
active: false
addCloudCredential: false
builtin: false
checksum: ""
description: ""
displayName: ionoscloud
externalId: ""
uiUrl: ""
url: https://github.com/ionos-cloud/docker-machine-driver/releases/download/v6.1.4/docker-machine-driver-6.1.4-linux-amd64.tar.gz" > ionos.yaml
```
use kubetl to create a node driver resource
```
Expand Down
2 changes: 2 additions & 0 deletions docs/usage/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Available Options for the IONOS Cloud Docker Machine Driver:
| `--ionoscloud-datacenter-name` | Existing Ionos Cloud Virtual Data Center Name (string) in which to create the Docker Host |
| `--ionoscloud-lan-id` | Existing Ionos Cloud LAN ID (numeric) in which to create the Docker Host |
| `--ionoscloud-lan-name` | Existing Ionos Cloud LAN Name (string) in which to create the Docker Host |
| `--ionoscloud-additional-lans` | Names of existing IONOS Lans to connect the machine to. Names that are not found are ignored |
| `--ionoscloud-disk-size` | Ionos Cloud Volume Disk-Size in GB \(10, 50, 100, 200, 400\) |
| `--ionoscloud-disk-type` | Ionos Cloud Volume Disk-Type \(HDD, SSD\) |
| `--ionoscloud-image` | Ionos Cloud Image Id or Alias \(ubuntu:latest, ubuntu:20.04\). If Image Id is set, please make sure the disk type supports the image type. |
Expand Down Expand Up @@ -88,6 +89,7 @@ Environment variables are also supported for setting options. This is a list of
| `--ionoscloud-datacenter-name` | `IONOSCLOUD_DATACENTER_NAME` |
| `--ionoscloud-lan-id` | `IONOSCLOUD_LAN_ID` |
| `--ionoscloud-lan-name` | `IONOSCLOUD_LAN_NAME` |
| `--ionoscloud-additional-lans` | `IONOSCLOUD_ADDITIONAL_LANS` |
| `--ionoscloud-disk-size` | `IONOSCLOUD_DISK_SIZE` |
| `--ionoscloud-disk-type` | `IONOSCLOUD_DISK_TYPE` |
| `--ionoscloud-image` | `IONOSCLOUD_IMAGE` |
Expand Down
10 changes: 10 additions & 0 deletions internal/utils/common_functions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package utils

func Contains(s []string, el string) bool {
for _, x := range s {
if x == el {
return true
}
}
return false
}
57 changes: 52 additions & 5 deletions ionoscloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ const (
flagSkipDefaultNatRules = "ionoscloud-skip-default-nat-rules"
flagNatLansToGateways = "ionoscloud-nat-lans-to-gateways"
flagPrivateLan = "ionoscloud-private-lan"
flagAdditionalLans = "ionoscloud-additional-lans"
flagCreateNat = "ionoscloud-create-nat"
// ---
)
Expand Down Expand Up @@ -125,6 +126,9 @@ type Driver struct {
ServerAvailabilityZone string
LanId string
LanName string
AdditionalLans []string
AdditionalLansIds []int
AdditionalNicsIds []string
DatacenterId string
DatacenterName string
VolumeId string
Expand Down Expand Up @@ -229,6 +233,11 @@ 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.StringSliceFlag{
Name: flagAdditionalLans,
EnvVar: extflag.KebabCaseToEnvVarCase(flagAdditionalLans),
Usage: "Names of existing IONOS Lans to connect the machine to. Names that are not found are ignored",
},
mcnflag.BoolFlag{
Name: flagWaitForIpChange,
EnvVar: extflag.KebabCaseToEnvVarCase(flagWaitForIpChange),
Expand Down Expand Up @@ -428,6 +437,7 @@ func (d *Driver) SetConfigFromFlags(opts drivers.DriverOptions) error {
d.SSHInCloudInit = opts.Bool(flagSSHInCloudInit)
d.CloudInitB64 = opts.String(flagCloudInitB64)
d.PrivateLan = opts.Bool(flagPrivateLan)
d.AdditionalLans = opts.StringSlice(flagAdditionalLans)

d.SwarmMaster = opts.Bool("swarm-master")
d.SwarmHost = opts.String("swarm-host")
Expand Down Expand Up @@ -466,12 +476,18 @@ func (d *Driver) PreCreateCheck() error {
d.LanExists = false
d.NatExists = false

for i := len(d.MachineName) - 1; i >= 0; i-- {
if !unicode.IsNumber(rune(d.MachineName[i])) {
if d.MachineName[i+1:] != "1" {
time.Sleep(60 * time.Second)
if strings.Contains(d.MachineName, "-pool") {
if !strings.Contains(d.MachineName, "-pool1-") {
time.Sleep(60 * time.Second)
}
} else {
for i := len(d.MachineName) - 1; i >= 0; i-- {
if !unicode.IsNumber(rune(d.MachineName[i])) {
if d.MachineName[i+1:] != "1" {
time.Sleep(60 * time.Second)
}
break
}
break
}
}
if d.DatacenterId == "" {
Expand Down Expand Up @@ -513,6 +529,14 @@ func (d *Driver) PreCreateCheck() error {
if lanId, ok := lan.GetIdOk(); ok && lanId != nil {
d.LanId = *lanId
}
} else if utils.Contains(d.AdditionalLans, *lan.Properties.Name) {
if lanId, ok := lan.GetIdOk(); ok && lanId != nil {
lanIdInt, err := strconv.Atoi(*lanId)
if err != nil {
return fmt.Errorf("invalid LAN ID found: %v", *lanId)
}
d.AdditionalLansIds = append(d.AdditionalLansIds, lanIdInt)
}
}
}

Expand Down Expand Up @@ -847,6 +871,20 @@ func (d *Driver) Create() (err error) {
}
return err
}
for _, additionalLanId := range d.AdditionalLansIds {
additionalNic, err := d.client().CreateAttachNIC(d.DatacenterId, d.ServerId, d.MachineName, true, int32(additionalLanId), nil)
if err != nil {
// TODO: Duplicated
log.Warn(rollingBackNotice)
if removeErr := d.Remove(); removeErr != nil {
return fmt.Errorf("failed to create machine due to error: %w\n Removing created resources: %v", fmt.Errorf("error attaching additional NIC: %w", err), removeErr)
}
return err
}
if nicId, ok := additionalNic.GetIdOk(); ok && nicId != nil {
d.AdditionalNicsIds = append(d.AdditionalNicsIds, *nicId)
}
}
if nicId, ok := nic.GetIdOk(); ok && nicId != nil {
d.NicId = *nic.Id
log.Debugf("Nic ID: %v", d.NicId)
Expand Down Expand Up @@ -932,6 +970,15 @@ func (d *Driver) Remove() error {
d.NicId = ""
}
}
if d.DatacenterId != "" && d.ServerId != "" {
for _, additionalNicId := range d.AdditionalNicsIds {
log.Debugf("Starting deleting additional Nic with Id: %v", additionalNicId)
err := d.client().RemoveNic(d.DatacenterId, d.ServerId, additionalNicId)
if err != nil {
result = multierror.Append(result, fmt.Errorf("error deleting additional NIC: %w", err))
}
}
}
if d.DatacenterId != "" && d.VolumeId != "" {
log.Debugf("Starting deleting Volume with Id: %v", d.VolumeId)
err = d.client().RemoveVolume(d.DatacenterId, d.VolumeId)
Expand Down
70 changes: 70 additions & 0 deletions ionoscloud_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package ionoscloud
import (
"fmt"
"net"
"reflect"
"testing"

"github.com/docker/machine/libmachine/drivers"
Expand Down Expand Up @@ -30,6 +31,11 @@ var (
imageLocation = "us/las"
dcVersion = int32(1)
testErr = fmt.Errorf("error")
lanId1 = "2"
lanId1Int = 2
lanName1 = "test"
lanId2 = "5"
lanName2 = "test2"
)

var (
Expand Down Expand Up @@ -86,9 +92,20 @@ var (
Id: &testVar,
Properties: &sdkgo.LanProperties{Public: &testFalse},
}
privateLan2 = &sdkgo.Lan{
Id: &lanId1,
Properties: &sdkgo.LanProperties{Public: &testFalse, Name: &lanName1},
}
privateLan3 = &sdkgo.Lan{
Id: &lanId2,
Properties: &sdkgo.LanProperties{Public: &testFalse, Name: &lanName2},
}
lans = sdkgo.Lans{
Items: &[]sdkgo.Lan{},
}
additionalLans = sdkgo.Lans{
Items: &[]sdkgo.Lan{*privateLan2, *privateLan3},
}

server = &sdkgo.Server{
Id: &testVar,
Expand Down Expand Up @@ -306,6 +323,20 @@ func TestPreCreateCheck(t *testing.T) {
err := driver.PreCreateCheck()
assert.NoError(t, err)
}
func TestPreCreateLans(t *testing.T) {
driver, clientMock := NewTestDriverFlagsSet(t, authFlagsSet)
driver.DatacenterId = "test"
driver.AdditionalLans = []string{lanName1, "wrong_value"}
clientMock.EXPECT().GetDatacenters().Return(dcs, nil)
clientMock.EXPECT().GetLans(driver.DatacenterId).Return(&additionalLans, nil)
clientMock.EXPECT().GetDatacenter(driver.DatacenterId).Return(dc, nil)
clientMock.EXPECT().GetLocationById("us", "ewr").Return(location, nil)
clientMock.EXPECT().GetImageById(defaultImageAlias).Return(&sdkgo.Image{}, fmt.Errorf("no image found with this id"))
clientMock.EXPECT().GetImages().Return(&images, nil)
err := driver.PreCreateCheck()
assert.True(t, reflect.DeepEqual(driver.AdditionalLansIds, []int{lanId1Int}))
assert.NoError(t, err)
}

func TestCreateSSHKeyErr(t *testing.T) {
driver, _ := NewTestDriverFlagsSet(t, authFlagsSet)
Expand Down Expand Up @@ -390,6 +421,7 @@ func TestCreateNicDhcpIps(t *testing.T) {
driver.IPAddress = testVar
driver.NicDhcp = true
driver.NicIps = []string{"127.0.0.1", "127.0.0.3"}
driver.AdditionalLansIds = []int{1, 2}

attached_volumes := sdkgo.NewAttachedVolumesWithDefaults()
attached_volumes.Items = &[]sdkgo.Volume{*volume}
Expand All @@ -408,6 +440,8 @@ func TestCreateNicDhcpIps(t *testing.T) {
clientMock.EXPECT().CreateServer(driver.DatacenterId, gomock.AssignableToTypeOf(sdkgo.Server{})).Return(server, nil)
clientMock.EXPECT().GetServer(driver.DatacenterId, driver.ServerId).Return(server, nil)
clientMock.EXPECT().CreateAttachNIC(driver.DatacenterId, driver.ServerId, driver.MachineName, true, int32(0), &driver.NicIps).Return(nic, nil)
clientMock.EXPECT().CreateAttachNIC(driver.DatacenterId, driver.ServerId, driver.MachineName, true, int32(1), nil).Return(nic, nil)
clientMock.EXPECT().CreateAttachNIC(driver.DatacenterId, driver.ServerId, driver.MachineName, true, int32(2), nil).Return(nic, nil)
err := driver.Create()
assert.NoError(t, err)
}
Expand Down Expand Up @@ -742,6 +776,42 @@ func TestCreateAttachNicErr(t *testing.T) {
assert.Error(t, err)
}

func TestCreateAttachAdditionalNicErr(t *testing.T) {
driver, clientMock := NewTestDriverFlagsSet(t, authFlagsSet)
driver.SSHKey = testVar
driver.DatacenterId = testVar
driver.ServerId = testVar
driver.NicId = testVar
driver.VolumeId = testVar
driver.LanId = testVar
driver.IPAddress = testVar
driver.IpBlockId = testVar
driver.AdditionalLansIds = []int{2, 4}
clientMock.EXPECT().GetLocationById("us", "las").Return(location, nil)
clientMock.EXPECT().GetImageById(defaultImageAlias).Return(&sdkgo.Image{}, fmt.Errorf("no image found with this id"))
clientMock.EXPECT().GetImages().Return(&images, nil)
clientMock.EXPECT().CreateIpBlock(int32(1), driver.Location).Return(ipblock, nil)
clientMock.EXPECT().GetDatacenter(driver.DatacenterId).Return(dc, nil)
clientMock.EXPECT().GetLan(gomock.AssignableToTypeOf("dc"), gomock.AssignableToTypeOf("lan")).Return(lan1, nil)
clientMock.EXPECT().UpdateCloudInitFile(driver.CloudInit, "hostname", []interface{}{driver.MachineName}, true, "skip").Return(driver.CloudInit, nil)
clientMock.EXPECT().GetNic(gomock.AssignableToTypeOf("dc"), gomock.AssignableToTypeOf("sv"), gomock.AssignableToTypeOf("nic")).Return(nic, nil)
clientMock.EXPECT().CreateServer(driver.DatacenterId, gomock.AssignableToTypeOf(sdkgo.Server{})).Return(server, nil)
clientMock.EXPECT().GetServer(driver.DatacenterId, driver.ServerId).Return(server, nil)
clientMock.EXPECT().CreateAttachVolume(driver.DatacenterId, driver.ServerId, propertiesImageId).Return(volume, nil)
clientMock.EXPECT().GetIpBlockIps(ipblock).Return(&ips, nil)
clientMock.EXPECT().CreateAttachNIC(driver.DatacenterId, driver.ServerId, driver.MachineName, false, int32(0), &ips).Return(nic, nil)
clientMock.EXPECT().CreateAttachNIC(driver.DatacenterId, driver.ServerId, driver.MachineName, true, int32(2), nil).Return(nic, nil)
clientMock.EXPECT().CreateAttachNIC(driver.DatacenterId, driver.ServerId, driver.MachineName, true, int32(4), nil).Return(nic, testErr)
clientMock.EXPECT().RemoveNic(driver.DatacenterId, driver.ServerId, driver.NicId).Return(testErr)
clientMock.EXPECT().RemoveNic(driver.DatacenterId, driver.ServerId, driver.NicId).Return(testErr)
clientMock.EXPECT().RemoveVolume(driver.DatacenterId, driver.VolumeId).Return(testErr)
clientMock.EXPECT().RemoveServer(driver.DatacenterId, driver.ServerId).Return(testErr)
clientMock.EXPECT().RemoveLan(driver.DatacenterId, driver.LanId).Return(testErr)
clientMock.EXPECT().RemoveIpBlock(driver.IpBlockId).Return(testErr)
err := driver.Create()
assert.Error(t, err)
}

func TestRemove(t *testing.T) {
driver, clientMock := NewTestDriverFlagsSet(t, authFlagsSet)
driver.DatacenterId = testVar
Expand Down
Loading