1
0
mirror of https://github.com/rancher/os.git synced 2025-09-03 15:54:24 +00:00

refactor a little and keep the datasource errors for later

Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>
This commit is contained in:
Sven Dowideit
2017-03-01 15:12:56 +10:00
parent 23a4d8ec76
commit 391082fa50
16 changed files with 181 additions and 91 deletions

View File

@@ -20,12 +20,10 @@ import (
"os" "os"
"strings" "strings"
"sync" "sync"
"syscall"
"time" "time"
yaml "github.com/cloudfoundry-incubator/candiedyaml" yaml "github.com/cloudfoundry-incubator/candiedyaml"
"github.com/docker/docker/pkg/mount"
"github.com/rancher/os/cmd/control" "github.com/rancher/os/cmd/control"
"github.com/rancher/os/cmd/network" "github.com/rancher/os/cmd/network"
rancherConfig "github.com/rancher/os/config" rancherConfig "github.com/rancher/os/config"
@@ -49,9 +47,6 @@ const (
datasourceInterval = 100 * time.Millisecond datasourceInterval = 100 * time.Millisecond
datasourceMaxInterval = 30 * time.Second datasourceMaxInterval = 30 * time.Second
datasourceTimeout = 5 * time.Minute datasourceTimeout = 5 * time.Minute
configDevName = "config-2"
configDev = "LABEL=" + configDevName
configDevMountPoint = "/media/config-2"
) )
func Main() { func Main() {
@@ -70,29 +65,8 @@ func Main() {
} }
} }
func MountConfigDrive() error {
if err := os.MkdirAll(configDevMountPoint, 644); err != nil {
return err
}
configDev := util.ResolveDevice(configDev)
if configDev == "" {
return mount.Mount(configDevName, configDevMountPoint, "9p", "trans=virtio,version=9p2000.L")
}
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)
}
func SaveCloudConfig(network bool) error { func SaveCloudConfig(network bool) error {
log.Debugf("SaveCloudConfig")
userDataBytes, metadata, err := fetchUserData(network) userDataBytes, metadata, err := fetchUserData(network)
if err != nil { if err != nil {
return err return err
@@ -178,6 +152,7 @@ func currentDatasource(network bool) (datasource.Datasource, error) {
dss := getDatasources(cfg, network) dss := getDatasources(cfg, network)
if len(dss) == 0 { if len(dss) == 0 {
log.Errorf("currentDatasource - none found")
return nil, nil return nil, nil
} }
@@ -299,11 +274,13 @@ func selectDatasource(sources []datasource.Datasource) datasource.Datasource {
duration := datasourceInterval duration := datasourceInterval
for { for {
log.Infof("Checking availability of %q\n", s.Type()) log.Infof("cloud-init: Checking availability of %q\n", s.Type())
if s.IsAvailable() { if s.IsAvailable() {
ds <- s ds <- s
return return
} else if !s.AvailabilityChanges() { }
log.Errorf("cloud-init: Datasource not ready: %s", s)
if !s.AvailabilityChanges() {
return return
} }
select { select {

76
config/cloudinit/datasource/configdrive/configdrive.go Normal file → Executable file
View File

@@ -16,30 +16,59 @@ package configdrive
import ( import (
"encoding/json" "encoding/json"
"fmt"
"io/ioutil" "io/ioutil"
"log"
"os" "os"
"path" "path"
"syscall"
"github.com/rancher/os/log"
"github.com/docker/docker/pkg/mount"
"github.com/rancher/os/config/cloudinit/datasource" "github.com/rancher/os/config/cloudinit/datasource"
"github.com/rancher/os/util"
) )
const ( const (
configDevName = "config-2"
configDev = "LABEL=" + configDevName
configDevMountPoint = "/media/config-2"
openstackAPIVersion = "latest" openstackAPIVersion = "latest"
) )
type ConfigDrive struct { type ConfigDrive struct {
root string root string
readFile func(filename string) ([]byte, error) readFile func(filename string) ([]byte, error)
lastError error
} }
func NewDatasource(root string) *ConfigDrive { func NewDatasource(root string) *ConfigDrive {
return &ConfigDrive{root, ioutil.ReadFile} return &ConfigDrive{root, ioutil.ReadFile, nil}
} }
func (cd *ConfigDrive) IsAvailable() bool { func (cd *ConfigDrive) IsAvailable() bool {
_, err := os.Stat(cd.root) if cd.root == configDevMountPoint {
return !os.IsNotExist(err) cd.lastError = MountConfigDrive()
if cd.lastError != nil {
log.Error(cd.lastError)
return false
}
defer cd.Finish()
}
_, cd.lastError = os.Stat(cd.root)
return !os.IsNotExist(cd.lastError)
}
func (cd *ConfigDrive) Finish() error {
return UnmountConfigDrive()
}
func (cd *ConfigDrive) String() string {
if cd.lastError != nil {
return fmt.Sprintf("%s: %s (lastError: %s)", cd.Type(), cd.root, cd.lastError)
}
return fmt.Sprintf("%s: %s", cd.Type(), cd.root)
} }
func (cd *ConfigDrive) AvailabilityChanges() bool { func (cd *ConfigDrive) AvailabilityChanges() bool {
@@ -93,10 +122,45 @@ func (cd *ConfigDrive) openstackVersionRoot() string {
} }
func (cd *ConfigDrive) tryReadFile(filename string) ([]byte, error) { func (cd *ConfigDrive) tryReadFile(filename string) ([]byte, error) {
if cd.root == configDevMountPoint {
cd.lastError = MountConfigDrive()
if cd.lastError != nil {
log.Error(cd.lastError)
return nil, cd.lastError
}
defer cd.Finish()
}
log.Printf("Attempting to read from %q\n", filename) log.Printf("Attempting to read from %q\n", filename)
data, err := cd.readFile(filename) data, err := cd.readFile(filename)
if os.IsNotExist(err) { if os.IsNotExist(err) {
err = nil err = nil
} }
if err != nil {
log.Errorf("ERROR read cloud-config file(%s) - err: %q", filename, err)
} else {
log.Debugf("SUCCESS read cloud-config file(%s) - date: %s", filename, string(data))
}
return data, err return data, err
} }
func MountConfigDrive() error {
if err := os.MkdirAll(configDevMountPoint, 644); err != nil {
return err
}
configDev := util.ResolveDevice(configDev)
if configDev == "" {
return mount.Mount(configDevName, configDevMountPoint, "9p", "trans=virtio,version=9p2000.L")
}
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)
}

View File

@@ -57,7 +57,7 @@ func TestFetchMetadata(t *testing.T) {
}, },
}, },
} { } {
cd := ConfigDrive{tt.root, tt.files.ReadFile} cd := ConfigDrive{tt.root, tt.files.ReadFile, nil}
metadata, err := cd.FetchMetadata() metadata, err := cd.FetchMetadata()
if err != nil { if err != nil {
t.Fatalf("bad error for %+v: want %v, got %q", tt, nil, err) t.Fatalf("bad error for %+v: want %v, got %q", tt, nil, err)
@@ -91,7 +91,7 @@ func TestFetchUserdata(t *testing.T) {
"userdata", "userdata",
}, },
} { } {
cd := ConfigDrive{tt.root, tt.files.ReadFile} cd := ConfigDrive{tt.root, tt.files.ReadFile, nil}
userdata, err := cd.FetchUserdata() userdata, err := cd.FetchUserdata()
if err != nil { if err != nil {
t.Fatalf("bad error for %+v: want %v, got %q", tt, nil, err) t.Fatalf("bad error for %+v: want %v, got %q", tt, nil, err)
@@ -116,7 +116,7 @@ func TestConfigRoot(t *testing.T) {
"/media/configdrive/openstack", "/media/configdrive/openstack",
}, },
} { } {
cd := ConfigDrive{tt.root, nil} cd := ConfigDrive{tt.root, nil, nil}
if configRoot := cd.ConfigRoot(); configRoot != tt.configRoot { if configRoot := cd.ConfigRoot(); configRoot != tt.configRoot {
t.Fatalf("bad config root for %q: want %q, got %q", tt, tt.configRoot, configRoot) t.Fatalf("bad config root for %q: want %q, got %q", tt, tt.configRoot, configRoot)
} }

3
config/cloudinit/datasource/datasource.go Normal file → Executable file
View File

@@ -25,6 +25,9 @@ type Datasource interface {
FetchMetadata() (Metadata, error) FetchMetadata() (Metadata, error)
FetchUserdata() ([]byte, error) FetchUserdata() ([]byte, error)
Type() string Type() string
String() string
// Finish gives the datasource the oportunity to clean up, unmount or release any open / cache resources
Finish() error
} }
type Metadata struct { type Metadata struct {

18
config/cloudinit/datasource/file/file.go Normal file → Executable file
View File

@@ -15,6 +15,7 @@
package file package file
import ( import (
"fmt"
"io/ioutil" "io/ioutil"
"os" "os"
@@ -22,16 +23,25 @@ import (
) )
type LocalFile struct { type LocalFile struct {
path string path string
lastError error
} }
func NewDatasource(path string) *LocalFile { func NewDatasource(path string) *LocalFile {
return &LocalFile{path} return &LocalFile{path, nil}
} }
func (f *LocalFile) IsAvailable() bool { func (f *LocalFile) IsAvailable() bool {
_, err := os.Stat(f.path) _, f.lastError = os.Stat(f.path)
return !os.IsNotExist(err) return !os.IsNotExist(f.lastError)
}
func (f *LocalFile) Finish() error {
return nil
}
func (f *LocalFile) String() string {
return fmt.Sprintf("%s: %s (lastError: %s)", f.Type(), f.path, f.lastError)
} }
func (f *LocalFile) AvailabilityChanges() bool { func (f *LocalFile) AvailabilityChanges() bool {

16
config/cloudinit/datasource/metadata/metadata.go Normal file → Executable file
View File

@@ -15,6 +15,7 @@
package metadata package metadata
import ( import (
"fmt"
"net/http" "net/http"
"strings" "strings"
@@ -27,18 +28,27 @@ type Service struct {
APIVersion string APIVersion string
UserdataPath string UserdataPath string
MetadataPath string MetadataPath string
lastError error
} }
func NewDatasource(root, apiVersion, userdataPath, metadataPath string, header http.Header) Service { func NewDatasource(root, apiVersion, userdataPath, metadataPath string, header http.Header) Service {
if !strings.HasSuffix(root, "/") { if !strings.HasSuffix(root, "/") {
root += "/" root += "/"
} }
return Service{root, pkg.NewHTTPClientHeader(header), apiVersion, userdataPath, metadataPath} return Service{root, pkg.NewHTTPClientHeader(header), apiVersion, userdataPath, metadataPath, nil}
} }
func (ms Service) IsAvailable() bool { func (ms Service) IsAvailable() bool {
_, err := ms.Client.Get(ms.Root + ms.APIVersion) _, ms.lastError = ms.Client.Get(ms.Root + ms.APIVersion)
return (err == nil) return (ms.lastError == nil)
}
func (ms *Service) Finish() error {
return nil
}
func (ms *Service) String() string {
return fmt.Sprintf("%s: %s (lastError: %s)", "metadata", ms.Root+":"+ms.UserdataPath, ms.lastError)
} }
func (ms Service) AvailabilityChanges() bool { func (ms Service) AvailabilityChanges() bool {

24
config/cloudinit/datasource/proccmdline/proc_cmdline.go Normal file → Executable file
View File

@@ -16,10 +16,12 @@ package proccmdline
import ( import (
"errors" "errors"
"fmt"
"io/ioutil" "io/ioutil"
"log"
"strings" "strings"
"github.com/rancher/os/log"
"github.com/rancher/os/config/cloudinit/datasource" "github.com/rancher/os/config/cloudinit/datasource"
"github.com/rancher/os/config/cloudinit/pkg" "github.com/rancher/os/config/cloudinit/pkg"
) )
@@ -30,7 +32,8 @@ const (
) )
type ProcCmdline struct { type ProcCmdline struct {
Location string Location string
lastError error
} }
func NewDatasource() *ProcCmdline { func NewDatasource() *ProcCmdline {
@@ -38,14 +41,23 @@ func NewDatasource() *ProcCmdline {
} }
func (c *ProcCmdline) IsAvailable() bool { func (c *ProcCmdline) IsAvailable() bool {
contents, err := ioutil.ReadFile(c.Location) var contents []byte
if err != nil { contents, c.lastError = ioutil.ReadFile(c.Location)
if c.lastError != nil {
return false return false
} }
cmdline := strings.TrimSpace(string(contents)) cmdline := strings.TrimSpace(string(contents))
_, err = findCloudConfigURL(cmdline) _, c.lastError = findCloudConfigURL(cmdline)
return (err == nil) return (c.lastError == nil)
}
func (c *ProcCmdline) Finish() error {
return nil
}
func (c *ProcCmdline) String() string {
return fmt.Sprintf("%s: %s (lastError: %s)", c.Type(), c.Location, c.lastError)
} }
func (c *ProcCmdline) AvailabilityChanges() bool { func (c *ProcCmdline) AvailabilityChanges() bool {

0
config/cloudinit/datasource/test/filesystem.go Normal file → Executable file
View File

19
config/cloudinit/datasource/url/url.go Normal file → Executable file
View File

@@ -15,22 +15,33 @@
package url package url
import ( import (
"fmt"
"github.com/rancher/os/config/cloudinit/datasource" "github.com/rancher/os/config/cloudinit/datasource"
"github.com/rancher/os/config/cloudinit/pkg" "github.com/rancher/os/config/cloudinit/pkg"
) )
type RemoteFile struct { type RemoteFile struct {
url string url string
lastError error
} }
func NewDatasource(url string) *RemoteFile { func NewDatasource(url string) *RemoteFile {
return &RemoteFile{url} return &RemoteFile{url, nil}
} }
func (f *RemoteFile) IsAvailable() bool { func (f *RemoteFile) IsAvailable() bool {
client := pkg.NewHTTPClient() client := pkg.NewHTTPClient()
_, err := client.Get(f.url) _, f.lastError = client.Get(f.url)
return (err == nil) return (f.lastError == nil)
}
func (f *RemoteFile) Finish() error {
return nil
}
func (f *RemoteFile) String() string {
return fmt.Sprintf("%s: %s (lastError: %s)", f.Type(), f.url, f.lastError)
} }
func (f *RemoteFile) AvailabilityChanges() bool { func (f *RemoteFile) AvailabilityChanges() bool {

9
config/cloudinit/datasource/vmware/vmware.go Normal file → Executable file
View File

@@ -29,6 +29,15 @@ type VMWare struct {
ovfFileName string ovfFileName string
readConfig readConfigFunction readConfig readConfigFunction
urlDownload urlDownloadFunction urlDownload urlDownloadFunction
lastError error
}
func (v VMWare) Finish() error {
return nil
}
func (v VMWare) String() string {
return fmt.Sprintf("%s: %s (lastError: %s)", v.Type(), v.ovfFileName, v.lastError)
} }
func (v VMWare) AvailabilityChanges() bool { func (v VMWare) AvailabilityChanges() bool {

9
config/cloudinit/datasource/vmware/vmware_amd64.go Normal file → Executable file
View File

@@ -16,14 +16,15 @@ package vmware
import ( import (
"io/ioutil" "io/ioutil"
"log"
"os" "os"
"github.com/rancher/os/log"
"github.com/rancher/os/config/cloudinit/pkg" "github.com/rancher/os/config/cloudinit/pkg"
"github.com/sigma/vmw-guestinfo/rpcvmx" "github.com/sigma/vmw-guestinfo/rpcvmx"
"github.com/sigma/vmw-guestinfo/vmcheck" "github.com/sigma/vmw-guestinfo/vmcheck"
"github.com/sigma/vmw-ovflib" ovf "github.com/sigma/vmw-ovflib"
) )
type ovfWrapper struct { type ovfWrapper struct {
@@ -69,8 +70,8 @@ func NewDatasource(fileName string) *VMWare {
func (v VMWare) IsAvailable() bool { func (v VMWare) IsAvailable() bool {
if v.ovfFileName != "" { if v.ovfFileName != "" {
_, err := os.Stat(v.ovfFileName) _, v.lastError = os.Stat(v.ovfFileName)
return !os.IsNotExist(err) return !os.IsNotExist(v.lastError)
} }
return vmcheck.IsVirtualWorld() return vmcheck.IsVirtualWorld()
} }

23
config/cloudinit/datasource/waagent/waagent.go Normal file → Executable file
View File

@@ -16,27 +16,38 @@ package waagent
import ( import (
"encoding/xml" "encoding/xml"
"fmt"
"io/ioutil" "io/ioutil"
"log"
"net" "net"
"os" "os"
"path" "path"
"github.com/rancher/os/log"
"github.com/rancher/os/config/cloudinit/datasource" "github.com/rancher/os/config/cloudinit/datasource"
) )
type Waagent struct { type Waagent struct {
root string root string
readFile func(filename string) ([]byte, error) readFile func(filename string) ([]byte, error)
lastError error
} }
func NewDatasource(root string) *Waagent { func NewDatasource(root string) *Waagent {
return &Waagent{root, ioutil.ReadFile} return &Waagent{root, ioutil.ReadFile, nil}
} }
func (a *Waagent) IsAvailable() bool { func (a *Waagent) IsAvailable() bool {
_, err := os.Stat(path.Join(a.root, "provisioned")) _, a.lastError = os.Stat(path.Join(a.root, "provisioned"))
return !os.IsNotExist(err) return !os.IsNotExist(a.lastError)
}
func (a *Waagent) Finish() error {
return nil
}
func (a *Waagent) String() string {
return fmt.Sprintf("%s: %s (lastError: %s)", a.Type(), a.root, a.lastError)
} }
func (a *Waagent) AvailabilityChanges() bool { func (a *Waagent) AvailabilityChanges() bool {

View File

@@ -86,7 +86,7 @@ func TestFetchMetadata(t *testing.T) {
}, },
}, },
} { } {
a := Waagent{tt.root, tt.files.ReadFile} a := Waagent{tt.root, tt.files.ReadFile, nil}
metadata, err := a.FetchMetadata() metadata, err := a.FetchMetadata()
if err != nil { if err != nil {
t.Fatalf("bad error for %+v: want %v, got %q", tt, nil, err) t.Fatalf("bad error for %+v: want %v, got %q", tt, nil, err)
@@ -115,7 +115,7 @@ func TestFetchUserdata(t *testing.T) {
test.NewMockFilesystem(test.File{Path: "/var/lib/Waagent/CustomData", Contents: ""}), test.NewMockFilesystem(test.File{Path: "/var/lib/Waagent/CustomData", Contents: ""}),
}, },
} { } {
a := Waagent{tt.root, tt.files.ReadFile} a := Waagent{tt.root, tt.files.ReadFile, nil}
_, err := a.FetchUserdata() _, err := a.FetchUserdata()
if err != nil { if err != nil {
t.Fatalf("bad error for %+v: want %v, got %q", tt, nil, err) t.Fatalf("bad error for %+v: want %v, got %q", tt, nil, err)
@@ -137,7 +137,7 @@ func TestConfigRoot(t *testing.T) {
"/var/lib/Waagent", "/var/lib/Waagent",
}, },
} { } {
a := Waagent{tt.root, nil} a := Waagent{tt.root, nil, nil}
if configRoot := a.ConfigRoot(); configRoot != tt.configRoot { if configRoot := a.ConfigRoot(); configRoot != tt.configRoot {
t.Fatalf("bad config root for %q: want %q, got %q", tt, tt.configRoot, configRoot) t.Fatalf("bad config root for %q: want %q, got %q", tt, tt.configRoot, configRoot)
} }

24
init/init.go Normal file → Executable file
View File

@@ -12,7 +12,6 @@ import (
"syscall" "syscall"
"github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/mount"
"github.com/rancher/os/cmd/cloudinitsave"
"github.com/rancher/os/config" "github.com/rancher/os/config"
"github.com/rancher/os/dfs" "github.com/rancher/os/dfs"
"github.com/rancher/os/log" "github.com/rancher/os/log"
@@ -294,29 +293,10 @@ func RunInit() error {
log.Error(err) log.Error(err)
} }
network := false if err := runCloudInitServices(cfg); err != nil {
for _, datasource := range cfg.Rancher.CloudInit.Datasources { log.Error(err)
if cloudinitsave.RequiresNetwork(datasource) {
network = true
break
}
} }
if network {
if err := runCloudInitServices(cfg); err != nil {
log.Error(err)
}
} else {
if err := cloudinitsave.MountConfigDrive(); err != nil {
log.Error(err)
}
if err := cloudinitsave.SaveCloudConfig(false); err != nil {
log.Error(err)
}
if err := cloudinitsave.UnmountConfigDrive(); err != nil {
log.Error(err)
}
}
return cfg, nil return cfg, nil
}, },
func(cfg *config.CloudConfig) (*config.CloudConfig, error) { func(cfg *config.CloudConfig) (*config.CloudConfig, error) {

View File

@@ -1,4 +1,5 @@
rancher: rancher:
debug: true
environment: environment:
VERSION: {{.VERSION}} VERSION: {{.VERSION}}
SUFFIX: {{.SUFFIX}} SUFFIX: {{.SUFFIX}}
@@ -66,6 +67,7 @@ rancher:
cloud_init: cloud_init:
datasources: datasources:
- configdrive:/media/config-2 - configdrive:/media/config-2
- url:http://example.com/404-error
repositories: repositories:
core: core:
url: {{.OS_SERVICES_REPO}}/{{.REPO_VERSION}} url: {{.OS_SERVICES_REPO}}/{{.REPO_VERSION}}

View File

@@ -48,4 +48,4 @@ REBUILD=1
QEMUARCH=${qemuarch["${ARCH}"]} QEMUARCH=${qemuarch["${ARCH}"]}
TTYCONS=${ttycons["${ARCH}"]} TTYCONS=${ttycons["${ARCH}"]}
DEFAULT_KERNEL_ARGS="rancher.debug=false rancher.password=rancher console=${TTYCONS} rancher.autologin=${TTYCONS}" DEFAULT_KERNEL_ARGS="printk.devkmsg=on rancher.debug=true rancher.password=rancher console=${TTYCONS} rancher.autologin=${TTYCONS}"