Skip to content

Commit 8695599

Browse files
pierre-emmanuelJniusmallnan
authored andcommitted
Add Exoscale provider for Cloudinit (rancher#2891)
Signed-off-by: Pierre-Emmanuel Jacquier <pierre-emmanuel.jacquier@epitech.eu>
1 parent 6a840ff commit 8695599

File tree

4 files changed

+192
-1
lines changed

4 files changed

+192
-1
lines changed

cmd/cloudinitsave/cloudinitsave.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
"github.com/rancher/os/config/cloudinit/datasource/metadata/cloudstack"
3737
"github.com/rancher/os/config/cloudinit/datasource/metadata/digitalocean"
3838
"github.com/rancher/os/config/cloudinit/datasource/metadata/ec2"
39+
"github.com/rancher/os/config/cloudinit/datasource/metadata/exoscale"
3940
"github.com/rancher/os/config/cloudinit/datasource/metadata/gce"
4041
"github.com/rancher/os/config/cloudinit/datasource/metadata/packet"
4142
"github.com/rancher/os/config/cloudinit/datasource/proccmdline"
@@ -228,7 +229,9 @@ func getDatasources(datasources []string) []datasource.Datasource {
228229

229230
switch parts[0] {
230231
case "*":
231-
dss = append(dss, getDatasources([]string{"configdrive", "vmware", "ec2", "digitalocean", "packet", "gce", "cloudstack"})...)
232+
dss = append(dss, getDatasources([]string{"configdrive", "vmware", "ec2", "digitalocean", "packet", "gce", "cloudstack", "exoscale"})...)
233+
case "exoscale":
234+
dss = append(dss, exoscale.NewDatasource(root))
232235
case "cloudstack":
233236
for _, source := range cloudstack.NewDatasource(root) {
234237
dss = append(dss, source)
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package exoscale
2+
3+
import (
4+
"net"
5+
"strconv"
6+
"strings"
7+
8+
"github.com/rancher/os/config/cloudinit/datasource"
9+
"github.com/rancher/os/config/cloudinit/datasource/metadata"
10+
"github.com/rancher/os/config/cloudinit/pkg"
11+
"github.com/rancher/os/pkg/log"
12+
)
13+
14+
const (
15+
defaultAddress = "http://169.254.169.254/"
16+
apiVersion = "1.0/"
17+
userdataPath = apiVersion + "user-data"
18+
metadataPath = apiVersion + "meta-data/"
19+
)
20+
21+
type MetadataService struct {
22+
metadata.Service
23+
}
24+
25+
func NewDatasource(root string) *MetadataService {
26+
if root == "" {
27+
root = defaultAddress
28+
}
29+
30+
return &MetadataService{
31+
metadata.NewDatasourceWithCheckPath(
32+
root,
33+
apiVersion,
34+
metadataPath,
35+
userdataPath,
36+
metadataPath,
37+
nil,
38+
),
39+
}
40+
}
41+
42+
func (ms MetadataService) AvailabilityChanges() bool {
43+
// TODO: if it can't find the network, maybe we can start it?
44+
return false
45+
}
46+
47+
func (ms MetadataService) FetchMetadata() (datasource.Metadata, error) {
48+
metadata := datasource.Metadata{}
49+
50+
if sshKeys, err := ms.FetchAttributes("public-keys"); err == nil {
51+
metadata.SSHPublicKeys = map[string]string{}
52+
for i, sshkey := range sshKeys {
53+
log.Printf("Found SSH key %d", i)
54+
metadata.SSHPublicKeys[strconv.Itoa(i)] = sshkey
55+
}
56+
} else if _, ok := err.(pkg.ErrNotFound); !ok {
57+
return metadata, err
58+
}
59+
60+
if hostname, err := ms.FetchAttribute("local-hostname"); err == nil {
61+
metadata.Hostname = strings.Split(hostname, " ")[0]
62+
} else if _, ok := err.(pkg.ErrNotFound); !ok {
63+
return metadata, err
64+
}
65+
66+
if localAddr, err := ms.FetchAttribute("local-ipv4"); err == nil {
67+
metadata.PrivateIPv4 = net.ParseIP(localAddr)
68+
} else if _, ok := err.(pkg.ErrNotFound); !ok {
69+
return metadata, err
70+
}
71+
if publicAddr, err := ms.FetchAttribute("public-ipv4"); err == nil {
72+
metadata.PublicIPv4 = net.ParseIP(publicAddr)
73+
} else if _, ok := err.(pkg.ErrNotFound); !ok {
74+
return metadata, err
75+
}
76+
77+
return metadata, nil
78+
}
79+
80+
func (ms MetadataService) Type() string {
81+
return "exoscale-metadata-service"
82+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package exoscale
2+
3+
import (
4+
"fmt"
5+
"net"
6+
"reflect"
7+
"testing"
8+
9+
"github.com/rancher/os/config/cloudinit/datasource"
10+
"github.com/rancher/os/config/cloudinit/datasource/metadata"
11+
"github.com/rancher/os/config/cloudinit/datasource/metadata/test"
12+
"github.com/rancher/os/config/cloudinit/pkg"
13+
)
14+
15+
func TestType(t *testing.T) {
16+
want := "exoscale-metadata-service"
17+
if kind := (MetadataService{}).Type(); kind != want {
18+
t.Fatalf("bad type: want %q, got %q", want, kind)
19+
}
20+
}
21+
22+
func TestFetchMetadata(t *testing.T) {
23+
for _, tt := range []struct {
24+
root string
25+
metadataPath string
26+
resources map[string]string
27+
expect datasource.Metadata
28+
clientErr error
29+
expectErr error
30+
}{
31+
{
32+
root: "/",
33+
metadataPath: "1.0/meta-data/",
34+
resources: map[string]string{
35+
"/1.0/meta-data/local-hostname": "host",
36+
"/1.0/meta-data/local-ipv4": "1.2.3.4",
37+
"/1.0/meta-data/public-ipv4": "5.6.7.8",
38+
"/1.0/meta-data/public-keys": "key\n",
39+
},
40+
expect: datasource.Metadata{
41+
Hostname: "host",
42+
PrivateIPv4: net.ParseIP("1.2.3.4"),
43+
PublicIPv4: net.ParseIP("5.6.7.8"),
44+
SSHPublicKeys: map[string]string{"0": "key"},
45+
},
46+
},
47+
{
48+
root: "/",
49+
metadataPath: "1.0/meta-data/",
50+
resources: map[string]string{
51+
"/1.0/meta-data/local-hostname": "host domain another_domain",
52+
"/1.0/meta-data/local-ipv4": "21.2.3.4",
53+
"/1.0/meta-data/public-ipv4": "25.6.7.8",
54+
"/1.0/meta-data/public-keys": "key\n",
55+
},
56+
expect: datasource.Metadata{
57+
Hostname: "host",
58+
PrivateIPv4: net.ParseIP("21.2.3.4"),
59+
PublicIPv4: net.ParseIP("25.6.7.8"),
60+
SSHPublicKeys: map[string]string{"0": "key"},
61+
},
62+
},
63+
{
64+
clientErr: pkg.ErrTimeout{Err: fmt.Errorf("test error")},
65+
expectErr: pkg.ErrTimeout{Err: fmt.Errorf("test error")},
66+
},
67+
} {
68+
service := &MetadataService{metadata.Service{
69+
Root: tt.root,
70+
Client: &test.HTTPClient{Resources: tt.resources, Err: tt.clientErr},
71+
MetadataPath: tt.metadataPath,
72+
}}
73+
metadata, err := service.FetchMetadata()
74+
if Error(err) != Error(tt.expectErr) {
75+
t.Fatalf("bad error (%q): \nwant %q, \ngot %q\n", tt.resources, tt.expectErr, err)
76+
}
77+
if !reflect.DeepEqual(tt.expect, metadata) {
78+
t.Fatalf("bad fetch (%q): \nwant %#v, \ngot %#v\n", tt.resources, tt.expect, metadata)
79+
}
80+
}
81+
}
82+
83+
func Error(err error) string {
84+
if err != nil {
85+
return err.Error()
86+
}
87+
return ""
88+
}

pkg/init/cloudinit/cloudinit.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package cloudinit
22

33
import (
4+
"io/ioutil"
45
"path/filepath"
56
"strings"
67

@@ -27,6 +28,14 @@ func CloudInit(cfg *config.CloudConfig) (*config.CloudConfig, error) {
2728
cfg.Rancher.CloudInit.Datasources = append(cfg.Rancher.CloudInit.Datasources, hypervisor)
2829
}
2930

31+
exoscale, err := onlyExoscale()
32+
if err != nil {
33+
log.Error(err)
34+
}
35+
if exoscale {
36+
cfg.Rancher.CloudInit.Datasources = append([]string{"exoscale"}, cfg.Rancher.CloudInit.Datasources...)
37+
}
38+
3039
if len(cfg.Rancher.CloudInit.Datasources) == 0 {
3140
log.Info("No specific datasources, ignore cloudinit")
3241
return cfg, nil
@@ -133,3 +142,12 @@ func onlyDigitalOcean(datasources []string) bool {
133142
}
134143
return false
135144
}
145+
146+
func onlyExoscale() (bool, error) {
147+
f, err := ioutil.ReadFile("/sys/class/dmi/id/product_name")
148+
if err != nil {
149+
return false, err
150+
}
151+
152+
return strings.HasPrefix(string(f), "Exoscale"), nil
153+
}

0 commit comments

Comments
 (0)