From 391082fa500a41d4bb7897616b794f01cc91d63b Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Wed, 1 Mar 2017 15:12:56 +1000 Subject: [PATCH 1/5] refactor a little and keep the datasource errors for later Signed-off-by: Sven Dowideit --- cmd/cloudinitsave/cloudinitsave.go | 35 ++------- .../datasource/configdrive/configdrive.go | 76 +++++++++++++++++-- .../configdrive/configdrive_test.go | 6 +- config/cloudinit/datasource/datasource.go | 3 + config/cloudinit/datasource/file/file.go | 18 ++++- .../cloudinit/datasource/metadata/metadata.go | 16 +++- .../datasource/proccmdline/proc_cmdline.go | 24 ++++-- .../cloudinit/datasource/test/filesystem.go | 0 config/cloudinit/datasource/url/url.go | 19 ++++- config/cloudinit/datasource/vmware/vmware.go | 9 +++ .../datasource/vmware/vmware_amd64.go | 9 ++- .../cloudinit/datasource/waagent/waagent.go | 23 ++++-- .../datasource/waagent/waagent_test.go | 6 +- init/init.go | 24 +----- os-config.tpl.yml | 2 + scripts/run-common | 2 +- 16 files changed, 181 insertions(+), 91 deletions(-) mode change 100644 => 100755 config/cloudinit/datasource/configdrive/configdrive.go mode change 100644 => 100755 config/cloudinit/datasource/datasource.go mode change 100644 => 100755 config/cloudinit/datasource/file/file.go mode change 100644 => 100755 config/cloudinit/datasource/metadata/metadata.go mode change 100644 => 100755 config/cloudinit/datasource/proccmdline/proc_cmdline.go mode change 100644 => 100755 config/cloudinit/datasource/test/filesystem.go mode change 100644 => 100755 config/cloudinit/datasource/url/url.go mode change 100644 => 100755 config/cloudinit/datasource/vmware/vmware.go mode change 100644 => 100755 config/cloudinit/datasource/vmware/vmware_amd64.go mode change 100644 => 100755 config/cloudinit/datasource/waagent/waagent.go mode change 100644 => 100755 init/init.go diff --git a/cmd/cloudinitsave/cloudinitsave.go b/cmd/cloudinitsave/cloudinitsave.go index bc57a30b..3125e7cb 100755 --- a/cmd/cloudinitsave/cloudinitsave.go +++ b/cmd/cloudinitsave/cloudinitsave.go @@ -20,12 +20,10 @@ import ( "os" "strings" "sync" - "syscall" "time" yaml "github.com/cloudfoundry-incubator/candiedyaml" - "github.com/docker/docker/pkg/mount" "github.com/rancher/os/cmd/control" "github.com/rancher/os/cmd/network" rancherConfig "github.com/rancher/os/config" @@ -49,9 +47,6 @@ const ( datasourceInterval = 100 * time.Millisecond datasourceMaxInterval = 30 * time.Second datasourceTimeout = 5 * time.Minute - configDevName = "config-2" - configDev = "LABEL=" + configDevName - configDevMountPoint = "/media/config-2" ) 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 { + log.Debugf("SaveCloudConfig") userDataBytes, metadata, err := fetchUserData(network) if err != nil { return err @@ -178,6 +152,7 @@ func currentDatasource(network bool) (datasource.Datasource, error) { dss := getDatasources(cfg, network) if len(dss) == 0 { + log.Errorf("currentDatasource - none found") return nil, nil } @@ -299,11 +274,13 @@ func selectDatasource(sources []datasource.Datasource) datasource.Datasource { duration := datasourceInterval for { - log.Infof("Checking availability of %q\n", s.Type()) + log.Infof("cloud-init: Checking availability of %q\n", s.Type()) if s.IsAvailable() { ds <- s return - } else if !s.AvailabilityChanges() { + } + log.Errorf("cloud-init: Datasource not ready: %s", s) + if !s.AvailabilityChanges() { return } select { diff --git a/config/cloudinit/datasource/configdrive/configdrive.go b/config/cloudinit/datasource/configdrive/configdrive.go old mode 100644 new mode 100755 index 14e8d249..880aeb7c --- a/config/cloudinit/datasource/configdrive/configdrive.go +++ b/config/cloudinit/datasource/configdrive/configdrive.go @@ -16,30 +16,59 @@ package configdrive import ( "encoding/json" + "fmt" "io/ioutil" - "log" "os" "path" + "syscall" + "github.com/rancher/os/log" + + "github.com/docker/docker/pkg/mount" "github.com/rancher/os/config/cloudinit/datasource" + "github.com/rancher/os/util" ) const ( + configDevName = "config-2" + configDev = "LABEL=" + configDevName + configDevMountPoint = "/media/config-2" openstackAPIVersion = "latest" ) type ConfigDrive struct { - root string - readFile func(filename string) ([]byte, error) + root string + readFile func(filename string) ([]byte, error) + lastError error } func NewDatasource(root string) *ConfigDrive { - return &ConfigDrive{root, ioutil.ReadFile} + return &ConfigDrive{root, ioutil.ReadFile, nil} } func (cd *ConfigDrive) IsAvailable() bool { - _, err := os.Stat(cd.root) - return !os.IsNotExist(err) + if cd.root == configDevMountPoint { + 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 { @@ -93,10 +122,45 @@ func (cd *ConfigDrive) openstackVersionRoot() string { } 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) data, err := cd.readFile(filename) if os.IsNotExist(err) { 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 } + +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) +} diff --git a/config/cloudinit/datasource/configdrive/configdrive_test.go b/config/cloudinit/datasource/configdrive/configdrive_test.go index b3c976a7..6812a3da 100644 --- a/config/cloudinit/datasource/configdrive/configdrive_test.go +++ b/config/cloudinit/datasource/configdrive/configdrive_test.go @@ -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() if err != nil { t.Fatalf("bad error for %+v: want %v, got %q", tt, nil, err) @@ -91,7 +91,7 @@ func TestFetchUserdata(t *testing.T) { "userdata", }, } { - cd := ConfigDrive{tt.root, tt.files.ReadFile} + cd := ConfigDrive{tt.root, tt.files.ReadFile, nil} userdata, err := cd.FetchUserdata() if err != nil { 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", }, } { - cd := ConfigDrive{tt.root, nil} + cd := ConfigDrive{tt.root, nil, nil} if configRoot := cd.ConfigRoot(); configRoot != tt.configRoot { t.Fatalf("bad config root for %q: want %q, got %q", tt, tt.configRoot, configRoot) } diff --git a/config/cloudinit/datasource/datasource.go b/config/cloudinit/datasource/datasource.go old mode 100644 new mode 100755 index e459303c..363ee4a8 --- a/config/cloudinit/datasource/datasource.go +++ b/config/cloudinit/datasource/datasource.go @@ -25,6 +25,9 @@ type Datasource interface { FetchMetadata() (Metadata, error) FetchUserdata() ([]byte, error) 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 { diff --git a/config/cloudinit/datasource/file/file.go b/config/cloudinit/datasource/file/file.go old mode 100644 new mode 100755 index 5924d703..9478c7f7 --- a/config/cloudinit/datasource/file/file.go +++ b/config/cloudinit/datasource/file/file.go @@ -15,6 +15,7 @@ package file import ( + "fmt" "io/ioutil" "os" @@ -22,16 +23,25 @@ import ( ) type LocalFile struct { - path string + path string + lastError error } func NewDatasource(path string) *LocalFile { - return &LocalFile{path} + return &LocalFile{path, nil} } func (f *LocalFile) IsAvailable() bool { - _, err := os.Stat(f.path) - return !os.IsNotExist(err) + _, f.lastError = os.Stat(f.path) + 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 { diff --git a/config/cloudinit/datasource/metadata/metadata.go b/config/cloudinit/datasource/metadata/metadata.go old mode 100644 new mode 100755 index 00e5464b..6d513309 --- a/config/cloudinit/datasource/metadata/metadata.go +++ b/config/cloudinit/datasource/metadata/metadata.go @@ -15,6 +15,7 @@ package metadata import ( + "fmt" "net/http" "strings" @@ -27,18 +28,27 @@ type Service struct { APIVersion string UserdataPath string MetadataPath string + lastError error } func NewDatasource(root, apiVersion, userdataPath, metadataPath string, header http.Header) Service { if !strings.HasSuffix(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 { - _, err := ms.Client.Get(ms.Root + ms.APIVersion) - return (err == nil) + _, ms.lastError = ms.Client.Get(ms.Root + ms.APIVersion) + 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 { diff --git a/config/cloudinit/datasource/proccmdline/proc_cmdline.go b/config/cloudinit/datasource/proccmdline/proc_cmdline.go old mode 100644 new mode 100755 index bad49d18..805b2a94 --- a/config/cloudinit/datasource/proccmdline/proc_cmdline.go +++ b/config/cloudinit/datasource/proccmdline/proc_cmdline.go @@ -16,10 +16,12 @@ package proccmdline import ( "errors" + "fmt" "io/ioutil" - "log" "strings" + "github.com/rancher/os/log" + "github.com/rancher/os/config/cloudinit/datasource" "github.com/rancher/os/config/cloudinit/pkg" ) @@ -30,7 +32,8 @@ const ( ) type ProcCmdline struct { - Location string + Location string + lastError error } func NewDatasource() *ProcCmdline { @@ -38,14 +41,23 @@ func NewDatasource() *ProcCmdline { } func (c *ProcCmdline) IsAvailable() bool { - contents, err := ioutil.ReadFile(c.Location) - if err != nil { + var contents []byte + contents, c.lastError = ioutil.ReadFile(c.Location) + if c.lastError != nil { return false } cmdline := strings.TrimSpace(string(contents)) - _, err = findCloudConfigURL(cmdline) - return (err == nil) + _, c.lastError = findCloudConfigURL(cmdline) + 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 { diff --git a/config/cloudinit/datasource/test/filesystem.go b/config/cloudinit/datasource/test/filesystem.go old mode 100644 new mode 100755 diff --git a/config/cloudinit/datasource/url/url.go b/config/cloudinit/datasource/url/url.go old mode 100644 new mode 100755 index 6d011dae..7973ebb4 --- a/config/cloudinit/datasource/url/url.go +++ b/config/cloudinit/datasource/url/url.go @@ -15,22 +15,33 @@ package url import ( + "fmt" + "github.com/rancher/os/config/cloudinit/datasource" "github.com/rancher/os/config/cloudinit/pkg" ) type RemoteFile struct { - url string + url string + lastError error } func NewDatasource(url string) *RemoteFile { - return &RemoteFile{url} + return &RemoteFile{url, nil} } func (f *RemoteFile) IsAvailable() bool { client := pkg.NewHTTPClient() - _, err := client.Get(f.url) - return (err == nil) + _, f.lastError = client.Get(f.url) + 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 { diff --git a/config/cloudinit/datasource/vmware/vmware.go b/config/cloudinit/datasource/vmware/vmware.go old mode 100644 new mode 100755 index 2c5ba187..f1593aab --- a/config/cloudinit/datasource/vmware/vmware.go +++ b/config/cloudinit/datasource/vmware/vmware.go @@ -29,6 +29,15 @@ type VMWare struct { ovfFileName string readConfig readConfigFunction 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 { diff --git a/config/cloudinit/datasource/vmware/vmware_amd64.go b/config/cloudinit/datasource/vmware/vmware_amd64.go old mode 100644 new mode 100755 index c87e0899..f805f34b --- a/config/cloudinit/datasource/vmware/vmware_amd64.go +++ b/config/cloudinit/datasource/vmware/vmware_amd64.go @@ -16,14 +16,15 @@ package vmware import ( "io/ioutil" - "log" "os" + "github.com/rancher/os/log" + "github.com/rancher/os/config/cloudinit/pkg" "github.com/sigma/vmw-guestinfo/rpcvmx" "github.com/sigma/vmw-guestinfo/vmcheck" - "github.com/sigma/vmw-ovflib" + ovf "github.com/sigma/vmw-ovflib" ) type ovfWrapper struct { @@ -69,8 +70,8 @@ func NewDatasource(fileName string) *VMWare { func (v VMWare) IsAvailable() bool { if v.ovfFileName != "" { - _, err := os.Stat(v.ovfFileName) - return !os.IsNotExist(err) + _, v.lastError = os.Stat(v.ovfFileName) + return !os.IsNotExist(v.lastError) } return vmcheck.IsVirtualWorld() } diff --git a/config/cloudinit/datasource/waagent/waagent.go b/config/cloudinit/datasource/waagent/waagent.go old mode 100644 new mode 100755 index 96e35185..da481327 --- a/config/cloudinit/datasource/waagent/waagent.go +++ b/config/cloudinit/datasource/waagent/waagent.go @@ -16,27 +16,38 @@ package waagent import ( "encoding/xml" + "fmt" "io/ioutil" - "log" "net" "os" "path" + "github.com/rancher/os/log" + "github.com/rancher/os/config/cloudinit/datasource" ) type Waagent struct { - root string - readFile func(filename string) ([]byte, error) + root string + readFile func(filename string) ([]byte, error) + lastError error } func NewDatasource(root string) *Waagent { - return &Waagent{root, ioutil.ReadFile} + return &Waagent{root, ioutil.ReadFile, nil} } func (a *Waagent) IsAvailable() bool { - _, err := os.Stat(path.Join(a.root, "provisioned")) - return !os.IsNotExist(err) + _, a.lastError = os.Stat(path.Join(a.root, "provisioned")) + 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 { diff --git a/config/cloudinit/datasource/waagent/waagent_test.go b/config/cloudinit/datasource/waagent/waagent_test.go index 01e6af56..b455f6bd 100644 --- a/config/cloudinit/datasource/waagent/waagent_test.go +++ b/config/cloudinit/datasource/waagent/waagent_test.go @@ -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() if err != nil { 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: ""}), }, } { - a := Waagent{tt.root, tt.files.ReadFile} + a := Waagent{tt.root, tt.files.ReadFile, nil} _, err := a.FetchUserdata() if err != nil { 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", }, } { - a := Waagent{tt.root, nil} + a := Waagent{tt.root, nil, nil} if configRoot := a.ConfigRoot(); configRoot != tt.configRoot { t.Fatalf("bad config root for %q: want %q, got %q", tt, tt.configRoot, configRoot) } diff --git a/init/init.go b/init/init.go old mode 100644 new mode 100755 index 23bd27b4..4e86ec11 --- a/init/init.go +++ b/init/init.go @@ -12,7 +12,6 @@ import ( "syscall" "github.com/docker/docker/pkg/mount" - "github.com/rancher/os/cmd/cloudinitsave" "github.com/rancher/os/config" "github.com/rancher/os/dfs" "github.com/rancher/os/log" @@ -294,29 +293,10 @@ func RunInit() error { log.Error(err) } - network := false - for _, datasource := range cfg.Rancher.CloudInit.Datasources { - if cloudinitsave.RequiresNetwork(datasource) { - network = true - break - } + if err := runCloudInitServices(cfg); err != nil { + log.Error(err) } - 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 }, func(cfg *config.CloudConfig) (*config.CloudConfig, error) { diff --git a/os-config.tpl.yml b/os-config.tpl.yml index dcf3a9cb..6b696581 100644 --- a/os-config.tpl.yml +++ b/os-config.tpl.yml @@ -1,4 +1,5 @@ rancher: + debug: true environment: VERSION: {{.VERSION}} SUFFIX: {{.SUFFIX}} @@ -66,6 +67,7 @@ rancher: cloud_init: datasources: - configdrive:/media/config-2 + - url:http://example.com/404-error repositories: core: url: {{.OS_SERVICES_REPO}}/{{.REPO_VERSION}} diff --git a/scripts/run-common b/scripts/run-common index 79fb0af0..62b7475e 100755 --- a/scripts/run-common +++ b/scripts/run-common @@ -48,4 +48,4 @@ REBUILD=1 QEMUARCH=${qemuarch["${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}" From 78051c2814177e23ddc2a9f64259ad8cf4e02fb6 Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Wed, 1 Mar 2017 15:52:15 +1000 Subject: [PATCH 2/5] treat 404 error on datasource as unrecoverable Signed-off-by: Sven Dowideit --- cmd/cloudinitsave/cloudinitsave.go | 1 + config/cloudinit/datasource/url/url.go | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/cmd/cloudinitsave/cloudinitsave.go b/cmd/cloudinitsave/cloudinitsave.go index 3125e7cb..e7e10bb3 100755 --- a/cmd/cloudinitsave/cloudinitsave.go +++ b/cmd/cloudinitsave/cloudinitsave.go @@ -276,6 +276,7 @@ func selectDatasource(sources []datasource.Datasource) datasource.Datasource { for { log.Infof("cloud-init: Checking availability of %q\n", s.Type()) if s.IsAvailable() { + log.Infof("cloud-init: Datasource GOOD: %s", s) ds <- s return } diff --git a/config/cloudinit/datasource/url/url.go b/config/cloudinit/datasource/url/url.go index 7973ebb4..f00fe43a 100755 --- a/config/cloudinit/datasource/url/url.go +++ b/config/cloudinit/datasource/url/url.go @@ -45,6 +45,11 @@ func (f *RemoteFile) String() string { } func (f *RemoteFile) AvailabilityChanges() bool { + if f.lastError != nil { + if _, ok := f.lastError.(pkg.ErrNotFound); ok { + return false + } + } return true } From 10a4c59385ac3b42a76a6491c32498bf4bf80eb9 Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Thu, 2 Mar 2017 11:52:29 +1000 Subject: [PATCH 3/5] Make datasource.AvailabilityChanges() able to be dynamic so fail out for configdrive mount and url 404's faster Signed-off-by: Sven Dowideit --- cmd/cloudinitsave/cloudinitsave.go | 164 +++++++++--------- .../datasource/configdrive/configdrive.go | 18 +- .../configdrive/configdrive_test.go | 6 +- config/cloudinit/datasource/url/url.go | 4 +- os-config.tpl.yml | 1 - 5 files changed, 96 insertions(+), 97 deletions(-) mode change 100644 => 100755 config/cloudinit/datasource/configdrive/configdrive_test.go diff --git a/cmd/cloudinitsave/cloudinitsave.go b/cmd/cloudinitsave/cloudinitsave.go index e7e10bb3..06c277c4 100755 --- a/cmd/cloudinitsave/cloudinitsave.go +++ b/cmd/cloudinitsave/cloudinitsave.go @@ -67,11 +67,83 @@ func Main() { func SaveCloudConfig(network bool) error { log.Debugf("SaveCloudConfig") - userDataBytes, metadata, err := fetchUserData(network) + cfg := rancherConfig.LoadConfig() + + dss := getDatasources(cfg, network) + if len(dss) == 0 { + log.Errorf("currentDatasource - none found") + return nil + } + + selectDatasource(dss) + return nil +} + +func RequiresNetwork(datasource string) bool { + // TODO: move into the datasources (and metadatasources) + parts := strings.SplitN(datasource, ":", 2) + requiresNetwork, ok := map[string]bool{ + "ec2": true, + "file": false, + "url": true, + "cmdline": true, + "configdrive": false, + "digitalocean": true, + "gce": true, + "packet": true, + }[parts[0]] + return ok && requiresNetwork +} + +func saveFiles(cloudConfigBytes, scriptBytes []byte, metadata datasource.Metadata) error { + os.MkdirAll(rancherConfig.CloudConfigDir, os.ModeDir|0600) + + if len(scriptBytes) > 0 { + log.Infof("Writing to %s", rancherConfig.CloudConfigScriptFile) + if err := util.WriteFileAtomic(rancherConfig.CloudConfigScriptFile, scriptBytes, 500); err != nil { + log.Errorf("Error while writing file %s: %v", rancherConfig.CloudConfigScriptFile, err) + return err + } + } + + if len(cloudConfigBytes) > 0 { + if err := util.WriteFileAtomic(rancherConfig.CloudConfigBootFile, cloudConfigBytes, 400); err != nil { + return err + } + // Don't put secrets into the log + //log.Infof("Written to %s:\n%s", rancherConfig.CloudConfigBootFile, string(cloudConfigBytes)) + } + + metaDataBytes, err := yaml.Marshal(metadata) if err != nil { return err } + if err = util.WriteFileAtomic(rancherConfig.MetaDataFile, metaDataBytes, 400); err != nil { + return err + } + // Don't put secrets into the log + //log.Infof("Written to %s:\n%s", rancherConfig.MetaDataFile, string(metaDataBytes)) + + return nil +} + +func fetchAndSave(ds datasource.Datasource) error { + var metadata datasource.Metadata + + log.Infof("Fetching user-data from datasource %s", ds) + userDataBytes, err := ds.FetchUserdata() + if err != nil { + log.Errorf("Failed fetching user-data from datasource: %v", err) + return err + } + log.Infof("Fetching meta-data from datasource of type %v", ds.Type()) + metadata, err = ds.FetchMetadata() + if err != nil { + log.Errorf("Failed fetching meta-data from datasource: %v", err) + return err + } + userData := string(userDataBytes) scriptBytes := []byte{} @@ -101,87 +173,6 @@ func SaveCloudConfig(network bool) error { return saveFiles(userDataBytes, scriptBytes, metadata) } -func RequiresNetwork(datasource string) bool { - parts := strings.SplitN(datasource, ":", 2) - requiresNetwork, ok := map[string]bool{ - "ec2": true, - "file": false, - "url": true, - "cmdline": true, - "configdrive": false, - "digitalocean": true, - "gce": true, - "packet": true, - }[parts[0]] - return ok && requiresNetwork -} - -func saveFiles(cloudConfigBytes, scriptBytes []byte, metadata datasource.Metadata) error { - os.MkdirAll(rancherConfig.CloudConfigDir, os.ModeDir|0600) - - if len(scriptBytes) > 0 { - log.Infof("Writing to %s", rancherConfig.CloudConfigScriptFile) - if err := util.WriteFileAtomic(rancherConfig.CloudConfigScriptFile, scriptBytes, 500); err != nil { - log.Errorf("Error while writing file %s: %v", rancherConfig.CloudConfigScriptFile, err) - return err - } - } - - if len(cloudConfigBytes) > 0 { - if err := util.WriteFileAtomic(rancherConfig.CloudConfigBootFile, cloudConfigBytes, 400); err != nil { - return err - } - log.Infof("Written to %s:\n%s", rancherConfig.CloudConfigBootFile, string(cloudConfigBytes)) - } - - metaDataBytes, err := yaml.Marshal(metadata) - if err != nil { - return err - } - - if err = util.WriteFileAtomic(rancherConfig.MetaDataFile, metaDataBytes, 400); err != nil { - return err - } - log.Infof("Written to %s:\n%s", rancherConfig.MetaDataFile, string(metaDataBytes)) - - return nil -} - -func currentDatasource(network bool) (datasource.Datasource, error) { - cfg := rancherConfig.LoadConfig() - - dss := getDatasources(cfg, network) - if len(dss) == 0 { - log.Errorf("currentDatasource - none found") - return nil, nil - } - - ds := selectDatasource(dss) - return ds, nil -} - -func fetchUserData(network bool) ([]byte, datasource.Metadata, error) { - var metadata datasource.Metadata - ds, err := currentDatasource(network) - if err != nil || ds == nil { - log.Errorf("Failed to select datasource: %v", err) - return nil, metadata, err - } - log.Infof("Fetching user-data from datasource %v", ds.Type()) - userDataBytes, err := ds.FetchUserdata() - if err != nil { - log.Errorf("Failed fetching user-data from datasource: %v", err) - return nil, metadata, err - } - log.Infof("Fetching meta-data from datasource of type %v", ds.Type()) - metadata, err = ds.FetchMetadata() - if err != nil { - log.Errorf("Failed fetching meta-data from datasource: %v", err) - return nil, metadata, err - } - return userDataBytes, metadata, nil -} - // getDatasources creates a slice of possible Datasources for cloudinit based // on the different source command-line flags. func getDatasources(cfg *rancherConfig.CloudConfig, network bool) []datasource.Datasource { @@ -276,14 +267,15 @@ func selectDatasource(sources []datasource.Datasource) datasource.Datasource { for { log.Infof("cloud-init: Checking availability of %q\n", s.Type()) if s.IsAvailable() { - log.Infof("cloud-init: Datasource GOOD: %s", s) + log.Infof("cloud-init: Datasource available: %s", s) ds <- s return } - log.Errorf("cloud-init: Datasource not ready: %s", s) if !s.AvailabilityChanges() { + log.Infof("cloud-init: Datasource unavailable, skipping: %s", s) return } + log.Errorf("cloud-init: Datasource not ready, will retry: %s", s) select { case <-stop: return @@ -303,6 +295,10 @@ func selectDatasource(sources []datasource.Datasource) datasource.Datasource { var s datasource.Datasource select { case s = <-ds: + err := fetchAndSave(s) + if err != nil { + log.Errorf("Error fetching cloud-init datasource(%s): %s", s, err) + } case <-done: case <-time.After(datasourceTimeout): } diff --git a/config/cloudinit/datasource/configdrive/configdrive.go b/config/cloudinit/datasource/configdrive/configdrive.go index 880aeb7c..cb92067b 100755 --- a/config/cloudinit/datasource/configdrive/configdrive.go +++ b/config/cloudinit/datasource/configdrive/configdrive.go @@ -37,13 +37,14 @@ const ( ) type ConfigDrive struct { - root string - readFile func(filename string) ([]byte, error) - lastError error + root string + readFile func(filename string) ([]byte, error) + lastError error + availabilityChanges bool } func NewDatasource(root string) *ConfigDrive { - return &ConfigDrive{root, ioutil.ReadFile, nil} + return &ConfigDrive{root, ioutil.ReadFile, nil, true} } func (cd *ConfigDrive) IsAvailable() bool { @@ -51,6 +52,8 @@ func (cd *ConfigDrive) IsAvailable() bool { cd.lastError = MountConfigDrive() if cd.lastError != nil { log.Error(cd.lastError) + // Don't keep retrying if we can't mount + cd.availabilityChanges = false return false } defer cd.Finish() @@ -58,6 +61,7 @@ func (cd *ConfigDrive) IsAvailable() bool { _, cd.lastError = os.Stat(cd.root) return !os.IsNotExist(cd.lastError) + // TODO: consider changing IsNotExists to not-available _and_ does not change } func (cd *ConfigDrive) Finish() error { @@ -72,7 +76,7 @@ func (cd *ConfigDrive) String() string { } func (cd *ConfigDrive) AvailabilityChanges() bool { - return true + return cd.availabilityChanges } func (cd *ConfigDrive) ConfigRoot() string { @@ -130,15 +134,13 @@ func (cd *ConfigDrive) tryReadFile(filename string) ([]byte, error) { } defer cd.Finish() } - log.Printf("Attempting to read from %q\n", filename) + log.Debugf("Attempting to read from %q\n", filename) data, err := cd.readFile(filename) if os.IsNotExist(err) { 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 } diff --git a/config/cloudinit/datasource/configdrive/configdrive_test.go b/config/cloudinit/datasource/configdrive/configdrive_test.go old mode 100644 new mode 100755 index 6812a3da..73e7aa17 --- a/config/cloudinit/datasource/configdrive/configdrive_test.go +++ b/config/cloudinit/datasource/configdrive/configdrive_test.go @@ -57,7 +57,7 @@ func TestFetchMetadata(t *testing.T) { }, }, } { - cd := ConfigDrive{tt.root, tt.files.ReadFile, nil} + cd := ConfigDrive{tt.root, tt.files.ReadFile, nil, true} metadata, err := cd.FetchMetadata() if err != nil { t.Fatalf("bad error for %+v: want %v, got %q", tt, nil, err) @@ -91,7 +91,7 @@ func TestFetchUserdata(t *testing.T) { "userdata", }, } { - cd := ConfigDrive{tt.root, tt.files.ReadFile, nil} + cd := ConfigDrive{tt.root, tt.files.ReadFile, nil, true} userdata, err := cd.FetchUserdata() if err != nil { 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", }, } { - cd := ConfigDrive{tt.root, nil, nil} + cd := ConfigDrive{tt.root, nil, nil, true} if configRoot := cd.ConfigRoot(); configRoot != tt.configRoot { t.Fatalf("bad config root for %q: want %q, got %q", tt, tt.configRoot, configRoot) } diff --git a/config/cloudinit/datasource/url/url.go b/config/cloudinit/datasource/url/url.go index f00fe43a..97dbcd32 100755 --- a/config/cloudinit/datasource/url/url.go +++ b/config/cloudinit/datasource/url/url.go @@ -46,7 +46,9 @@ func (f *RemoteFile) String() string { func (f *RemoteFile) AvailabilityChanges() bool { if f.lastError != nil { - if _, ok := f.lastError.(pkg.ErrNotFound); ok { + // if we have a Network error, then we should retry. + // otherwise, we've made a request to the server, and its said nope. + if _, ok := f.lastError.(pkg.ErrNetwork); !ok { return false } } diff --git a/os-config.tpl.yml b/os-config.tpl.yml index 6b696581..8fd040e2 100644 --- a/os-config.tpl.yml +++ b/os-config.tpl.yml @@ -67,7 +67,6 @@ rancher: cloud_init: datasources: - configdrive:/media/config-2 - - url:http://example.com/404-error repositories: core: url: {{.OS_SERVICES_REPO}}/{{.REPO_VERSION}} From df32dfdc70cfff06aa24913d6a9ca23e3768aaeb Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Thu, 2 Mar 2017 14:30:13 +1000 Subject: [PATCH 4/5] try to debug Drone failures - I think its just timing out. Signed-off-by: Sven Dowideit --- tests/common_test.go | 6 ++++-- tests/network_on_boot_test.go | 0 tests/upgrade_test.go | 14 +++++++------- 3 files changed, 11 insertions(+), 9 deletions(-) mode change 100644 => 100755 tests/network_on_boot_test.go diff --git a/tests/common_test.go b/tests/common_test.go index 14fef530..b1b53cea 100755 --- a/tests/common_test.go +++ b/tests/common_test.go @@ -106,7 +106,8 @@ func (s *QemuSuite) WaitForSSH() error { if err = cmd.Run(); err == nil { break } - time.Sleep(500 * time.Millisecond) + fmt.Printf("s%d", i) + time.Sleep(time.Second) } if err != nil { @@ -126,6 +127,7 @@ func (s *QemuSuite) WaitForSSH() error { if err = cmd.Run(); err == nil { return nil } + fmt.Printf("d%d", i) time.Sleep(500 * time.Millisecond) } @@ -185,7 +187,7 @@ func (s *QemuSuite) LoadInstallerImage(c *C) { c.Assert(cmd.Run(), IsNil) } -func (s *QemuSuite) PullAndLoadInstallerImage(c *C, image string) { +func (s *QemuSuite) PullAndLoadImage(c *C, image string) { cmd := exec.Command("sh", "-c", fmt.Sprintf("docker pull %s", image)) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr diff --git a/tests/network_on_boot_test.go b/tests/network_on_boot_test.go old mode 100644 new mode 100755 diff --git a/tests/upgrade_test.go b/tests/upgrade_test.go index 0f63f79c..96ba9c1b 100755 --- a/tests/upgrade_test.go +++ b/tests/upgrade_test.go @@ -51,7 +51,7 @@ func (s *QemuSuite) DisableTestUpgradeInner(c *C) { c.Assert(Version, Equals, version) fmt.Printf("installing %s", startWithVersion) - s.PullAndLoadInstallerImage(c, fmt.Sprintf("rancher/os:%s", startWithVersion)) + s.PullAndLoadImage(c, fmt.Sprintf("rancher/os:%s", startWithVersion)) //ADD a custom append line and make sure its kept in the upgraded version too @@ -77,10 +77,10 @@ sync if console != "default" { // Can't preload the startWithVersion console image, as some don't exist by that name - not sure how to approach that - //s.PullAndLoadInstallerImage(c, fmt.Sprintf("rancher/os-%sconsole:%s", console, startWithVersion)) + //s.PullAndLoadImage(c, fmt.Sprintf("rancher/os-%sconsole:%s", console, startWithVersion)) // TODO: ouch. probably need to tag the dev / master version as latest cos this won't work // Need to pull the image here - if we do it at boot, then the test will fail. - s.PullAndLoadInstallerImage(c, fmt.Sprintf("rancher/os-%sconsole:%s", console, "v0.8.0-rc3")) + s.PullAndLoadImage(c, fmt.Sprintf("rancher/os-%sconsole:%s", console, "v0.8.0-rc3")) s.MakeCall(fmt.Sprintf("sudo ros console switch -f %s", console)) c.Assert(s.WaitForSSH(), IsNil) } @@ -111,7 +111,7 @@ func (s *QemuSuite) commonTestCode(c *C, startWithVersion, console string) { c.Assert(Version, Equals, version) fmt.Printf("installing %s", startWithVersion) - s.PullAndLoadInstallerImage(c, fmt.Sprintf("rancher/os:%s", startWithVersion)) + s.PullAndLoadImage(c, fmt.Sprintf("rancher/os:%s", startWithVersion)) //ADD a custom append line and make sure its kept in the upgraded version too @@ -137,13 +137,13 @@ sync if console != "default" { // Can't preload the startWithVersion console image, as some don't exist by that name - not sure how to approach that - //s.PullAndLoadInstallerImage(c, fmt.Sprintf("rancher/os-%sconsole:%s", console, startWithVersion)) + //s.PullAndLoadImage(c, fmt.Sprintf("rancher/os-%sconsole:%s", console, startWithVersion)) // TODO: ouch. probably need to tag the dev / master version as latest cos this won't work // Need to pull the image here - if we do it at boot, then the test will fail. if console == "alpine" { - s.PullAndLoadInstallerImage(c, fmt.Sprintf("rancher/os-%sconsole:%s", console, "v0.8.0-rc5")) + s.PullAndLoadImage(c, fmt.Sprintf("rancher/os-%sconsole:%s", console, "v0.8.0-rc5")) } else { - s.PullAndLoadInstallerImage(c, fmt.Sprintf("rancher/os-%sconsole:%s", console, "v0.8.0-rc3")) + s.PullAndLoadImage(c, fmt.Sprintf("rancher/os-%sconsole:%s", console, "v0.8.0-rc3")) } s.MakeCall(fmt.Sprintf("sudo ros console switch -f %s", console)) c.Assert(s.WaitForSSH(), IsNil) From ff98f274074ab01b2b59257a73e19624cc4afe1e Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Mon, 6 Mar 2017 15:02:24 +1000 Subject: [PATCH 5/5] use a smaller console to test Signed-off-by: Sven Dowideit --- tests/assets/test_18/cloud-config.yml | 2 +- tests/network_on_boot_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/assets/test_18/cloud-config.yml b/tests/assets/test_18/cloud-config.yml index 155a1d98..268940e2 100644 --- a/tests/assets/test_18/cloud-config.yml +++ b/tests/assets/test_18/cloud-config.yml @@ -1,6 +1,6 @@ #cloud-config rancher: - console: debian + console: alpine services: missing_image: image: tianon/true diff --git a/tests/network_on_boot_test.go b/tests/network_on_boot_test.go index f17dfe0a..d5ee948c 100755 --- a/tests/network_on_boot_test.go +++ b/tests/network_on_boot_test.go @@ -5,6 +5,6 @@ import . "gopkg.in/check.v1" func (s *QemuSuite) TestNetworkOnBoot(c *C) { s.RunQemu(c, "--cloud-config", "./tests/assets/test_18/cloud-config.yml", "-net", "nic,vlan=1,model=virtio") - s.CheckCall(c, "apt-get --version") + s.CheckCall(c, "apk --version") s.CheckCall(c, "sudo system-docker images | grep tianon/true") }