mirror of
https://github.com/rancher/os.git
synced 2025-07-17 16:41:04 +00:00
Initial cloud-init support for ProxmoxVE
This commit is contained in:
parent
27d4dd2b8b
commit
8ad6b10446
113
config/cloudinit/datasource/proxmox/proxmox.go
Normal file
113
config/cloudinit/datasource/proxmox/proxmox.go
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
package proxmox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/rancher/os/config/cloudinit/datasource"
|
||||||
|
"github.com/rancher/os/pkg/log"
|
||||||
|
"github.com/rancher/os/pkg/util"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/mount"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
configDev = "/dev/sr0"
|
||||||
|
configDevMountPoint = "/media/pve-config"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Proxmox struct {
|
||||||
|
root string
|
||||||
|
readFile func(filename string) ([]byte, error)
|
||||||
|
lastError error
|
||||||
|
availabilityChanges bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDataSource(root string) *Proxmox {
|
||||||
|
return &Proxmox{root, ioutil.ReadFile, nil, true}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pve *Proxmox) IsAvailable() bool {
|
||||||
|
if pve.root == configDevMountPoint {
|
||||||
|
pve.lastError = MountConfigDrive()
|
||||||
|
if pve.lastError != nil {
|
||||||
|
log.Error(pve.lastError)
|
||||||
|
pve.availabilityChanges = false
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer pve.Finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
_, pve.lastError = os.Stat(pve.root)
|
||||||
|
return !os.IsNotExist(pve.lastError)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pve *Proxmox) Finish() error {
|
||||||
|
return UnmountConfigDrive()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pve *Proxmox) String() string {
|
||||||
|
if pve.lastError != nil {
|
||||||
|
return fmt.Sprintf("%s: %s (lastError: %v)", pve.Type(), pve.root, pve.lastError)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s: %s", pve.Type(), pve.root)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pve *Proxmox) AvailabilityChanges() bool {
|
||||||
|
return pve.availabilityChanges
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pve *Proxmox) ConfigRoot() string {
|
||||||
|
return pve.root
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pve *Proxmox) FetchMetadata() (metadata datasource.Metadata, err error) {
|
||||||
|
return datasource.Metadata{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pve *Proxmox) FetchUserdata() ([]byte, error) {
|
||||||
|
return pve.tryReadFile(path.Join(pve.root, "user-data"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pve *Proxmox) Type() string {
|
||||||
|
return "proxmox"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pve *Proxmox) tryReadFile(filename string) ([]byte, error) {
|
||||||
|
if pve.root == configDevMountPoint {
|
||||||
|
pve.lastError = MountConfigDrive()
|
||||||
|
if pve.lastError != nil {
|
||||||
|
log.Error(pve.lastError)
|
||||||
|
return nil, pve.lastError
|
||||||
|
}
|
||||||
|
defer pve.Finish()
|
||||||
|
}
|
||||||
|
log.Debugf("Attempting to read from %q\n", filename)
|
||||||
|
data, err := pve.readFile(filename)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("ERROR read cloud-config file(%s) - err: %q", filename, err)
|
||||||
|
}
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func MountConfigDrive() error {
|
||||||
|
if err := os.MkdirAll(configDevMountPoint, 700); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fsType, err := util.GetFsType(configDev)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return mount.Mount(configDev, configDevMountPoint, fsType, "ro")
|
||||||
|
}
|
||||||
|
|
||||||
|
func UnmountConfigDrive() error {
|
||||||
|
return syscall.Unmount(configDevMountPoint, 0)
|
||||||
|
}
|
73
config/cloudinit/datasource/proxmox/proxmox_test.go
Normal file
73
config/cloudinit/datasource/proxmox/proxmox_test.go
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package proxmox
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestFetchUserdata(t *testing.T) {
|
||||||
|
for _, tt := range []struct {
|
||||||
|
root string
|
||||||
|
files test.MockFilesystem
|
||||||
|
userdata string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
root: "/",
|
||||||
|
files: test.NewMockFilesystem(),
|
||||||
|
userdata: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
root: "/media/pve-config",
|
||||||
|
files: test.NewMockFilesystem(test.File{Path: "/media/pve-config/user-data", Contents: "userdata"}),
|
||||||
|
userdata: "userdata",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
pve := Proxmox{tt.root, tt.files.ReadFile, nil, true}
|
||||||
|
userdata, err := pve.FetchUserdata()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("bad error for %+v: want %v, get %q", tt, nil, err)
|
||||||
|
}
|
||||||
|
if string(userdata) != tt.userdata {
|
||||||
|
t.Fatalf("bad userdata for %+v: want %q, got %q", tt, tt.userdata, userdata)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigRoot(t *testing.T) {
|
||||||
|
for _, tt := range []struct {
|
||||||
|
root string
|
||||||
|
configRoot string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
root: "/",
|
||||||
|
configRoot: "/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
root: "/media/pve-config",
|
||||||
|
configRoot: "/media/pve-config",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
pve := Proxmox{tt.root, nil, nil, true}
|
||||||
|
if configRoot := pve.ConfigRoot(); configRoot != tt.configRoot {
|
||||||
|
t.Fatalf("bad config root for %q: want %q, got %q", tt, tt.configRoot, configRoot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewDataSource(t *testing.T) {
|
||||||
|
for _, tt := range []struct {
|
||||||
|
root string
|
||||||
|
expectRoot string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
root: "",
|
||||||
|
expectRoot: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
root: "/media/pve-config",
|
||||||
|
expectRoot: "/media/pve-config",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
service := NewDataSource(tt.root)
|
||||||
|
if service.root != tt.expectRoot {
|
||||||
|
t.Fatalf("bad root (%q): want %q, got %q", tt.root, tt.expectRoot, service.root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -36,6 +36,14 @@ func CloudInit(cfg *config.CloudConfig) (*config.CloudConfig, error) {
|
|||||||
cfg.Rancher.CloudInit.Datasources = append([]string{"exoscale"}, cfg.Rancher.CloudInit.Datasources...)
|
cfg.Rancher.CloudInit.Datasources = append([]string{"exoscale"}, cfg.Rancher.CloudInit.Datasources...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
proxmox, err := onlyProxmox()
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
if proxmox {
|
||||||
|
cfg.Rancher.CloudInit.Datasources = append([]string{"proxmox"}, cfg.Rancher.CloutInit.Datasources...)
|
||||||
|
}
|
||||||
|
|
||||||
if len(cfg.Rancher.CloudInit.Datasources) == 0 {
|
if len(cfg.Rancher.CloudInit.Datasources) == 0 {
|
||||||
log.Info("No specific datasources, ignore cloudinit")
|
log.Info("No specific datasources, ignore cloudinit")
|
||||||
return cfg, nil
|
return cfg, nil
|
||||||
@ -151,3 +159,12 @@ func onlyExoscale() (bool, error) {
|
|||||||
|
|
||||||
return strings.HasPrefix(string(f), "Exoscale"), nil
|
return strings.HasPrefix(string(f), "Exoscale"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func onlyProxmox() (bool, error) {
|
||||||
|
f, err := ioutil.ReadFile("/sys/class/dmi/id/product_name")
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Contains(string(f), "Proxmox"), nil
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user