diff --git a/.travis.yml b/.travis.yml index 7fb5d3d66..e8df6a577 100644 --- a/.travis.yml +++ b/.travis.yml @@ -59,7 +59,7 @@ script: - go list ./... | grep -v github.com/01org/ciao/vendor | xargs go list -f '{{.Dir}}' | xargs gofmt -s -l | wc -l | xargs -I % bash -c "test % -eq 0" - sudo mkdir -p /var/lib/ciao/instances - sudo chmod 0777 /var/lib/ciao/instances - - test-cases -text -coverprofile /tmp/cover.out -short github.com/01org/ciao/ciao-launcher github.com/01org/ciao/ciao-scheduler github.com/01org/ciao/ciao-controller/... github.com/01org/ciao/payloads + - test-cases -text -coverprofile /tmp/cover.out -short github.com/01org/ciao/ciao-launcher github.com/01org/ciao/ciao-scheduler github.com/01org/ciao/ciao-controller/... github.com/01org/ciao/payloads github.com/01org/ciao/configuration - export GOROOT=`go env GOROOT` && sudo -E PATH=$PATH:$GOROOT/bin $GOPATH/bin/test-cases -text -coverprofile /tmp/cover.out -append-profile github.com/01org/ciao/ssntp - export GOROOT=`go env GOROOT` && export SNNET_ENV=198.51.100.0/24 && sudo -E PATH=$PATH:$GOROOT/bin $GOPATH/bin/test-cases -text -short -tags travis -coverprofile /tmp/cover.out -append-profile github.com/01org/ciao/networking/libsnnet diff --git a/ciao-scheduler/scheduler.go b/ciao-scheduler/scheduler.go index d8deeabd9..4d0e5ef16 100644 --- a/ciao-scheduler/scheduler.go +++ b/ciao-scheduler/scheduler.go @@ -37,6 +37,8 @@ var cacert = flag.String("cacert", "/etc/pki/ciao/CAcert-server-localhost.pem", var cpuprofile = flag.String("cpuprofile", "", "Write cpu profile to file") var heartbeat = flag.Bool("heartbeat", false, "Emit status heartbeat text") var logDir = "/var/lib/ciao/logs/scheduler" +var configURI = flag.String("configuration-uri", "file:///etc/ciao/configuration.yaml", + "Cluster configuration URI") type ssntpSchedulerServer struct { // user config overrides ------------------------------------------ @@ -1006,8 +1008,9 @@ func configSchedulerServer() (sched *ssntpSchedulerServer) { toggleDebug(sched) sched.config = &ssntp.Config{ - CAcert: *cacert, - Cert: *cert, + CAcert: *cacert, + Cert: *cert, + ConfigURI: *configURI, } setSSNTPForwardRules(sched) diff --git a/configuration/configuration.go b/configuration/configuration.go new file mode 100644 index 000000000..9240b2600 --- /dev/null +++ b/configuration/configuration.go @@ -0,0 +1,124 @@ +// +// Copyright (c) 2016 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package configuration + +import ( + "errors" + "fmt" + "net/url" + + "github.com/01org/ciao/payloads" + "gopkg.in/yaml.v2" +) + +// we can have values set to default, except for +// scheduler { storage_uri } +// controller { compute_ca, compute_cert, identity_user, identity_password } +// launcher { compute_net, mgmt_net } +// image_service { url } +// identity_service { url } +// +// so we need to have at least those values set in our config +// +// TODO: proper validation of values set in yaml setup +func validMinConf(conf *payloads.Configure) bool { + return (conf.Configure.Scheduler.ConfigStorageURI != "" && + conf.Configure.Controller.HTTPSCACert != "" && + conf.Configure.Controller.HTTPSKey != "" && + conf.Configure.Controller.IdentityUser != "" && + conf.Configure.Controller.IdentityPassword != "" && + conf.Configure.Launcher.ComputeNetwork != "" && + conf.Configure.Launcher.ManagementNetwork != "" && + conf.Configure.ImageService.URL != "" && + conf.Configure.IdentityService.URL != "") +} + +func fillDefaults(conf *payloads.Configure) { + conf.Configure.Scheduler.ConfigStorageType = payloads.Filesystem + conf.Configure.Controller.ComputePort = 8774 + conf.Configure.ImageService.Type = payloads.Glance + conf.Configure.IdentityService.Type = payloads.Keystone + conf.Configure.Launcher.DiskLimit = true + conf.Configure.Launcher.MemoryLimit = true +} + +// TODO: add etcd support related scheme(s) +func discoverDriver(uriStr string) (storageType payloads.StorageType, err error) { + uri, err := url.Parse(uriStr) + if err != nil { + return storageType, err + } + switch uri.Scheme { + case "file": + return payloads.Filesystem, nil + default: + return "", fmt.Errorf( + "Configuration URI Scheme '%s' not supported", uri.Scheme) + } +} + +// Payload fills the payloads.Configure struct passed in 'conf' +// with the values from the bytes given +func Payload(yamlConf []byte, conf *payloads.Configure) (err error) { + if yamlConf == nil { + return fmt.Errorf("Unable to retrieve configuration from empty definition") + } + err = yaml.Unmarshal(yamlConf, &conf) + if err != nil { + return err + } + return nil +} + +// Blob returns an array of bytes containing +// the cluster configuration. +func Blob(conf *payloads.Configure) (blob []byte, err error) { + if validMinConf(conf) == false { + return nil, errors.New( + "minimal configuration is not met or yaml is malformed") + } + blob, err = yaml.Marshal(&conf) + if err != nil { + return nil, err + } + return blob, nil +} + +// ExtractBlob returns a configuration payload. +// It could be used by the SSNTP server or some other entity. +func ExtractBlob(uri string) (payload []byte, err error) { + var d driver + driverType, err := discoverDriver(uri) + if err != nil { + return nil, err + } + switch driverType { + case payloads.Filesystem: + d = &file{} + case payloads.Etcd: + d = &etcd{} + } + conf, err := d.fetchConfiguration(uri) + if err != nil { + return nil, err + } + payload, err = Blob(&conf) + if err != nil { + return nil, err + } + return payload, nil +} diff --git a/configuration/configuration_test.go b/configuration/configuration_test.go new file mode 100644 index 000000000..f6daf7ead --- /dev/null +++ b/configuration/configuration_test.go @@ -0,0 +1,298 @@ +// +// Copyright (c) 2016 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package configuration + +import ( + "bytes" + "io/ioutil" + "syscall" + "testing" + + "github.com/01org/ciao/payloads" + "github.com/google/gofuzz" +) + +const badScheme = "badScheme://non-existent/path/nofile" +const invalidURI = "file://%z invalid uri with spaces" +const emptyPathURI = "file://" + +const keystoneURL = "http://keystone.example.com" +const glanceURL = "http://glance.example.com" +const computeNet = "192.168.1.110" +const mgmtNet = "192.168.1.111" +const storageURI = "/etc/ciao/configuration.yaml" +const identityUser = "controller" +const identityPassword = "ciao" +const httpsKey = "/etc/pki/ciao/compute_key.pem" +const httpsCACert = "/etc/pki/ciao/compute_ca.pem" + +const minValidConf = `configure: + scheduler: + storage_uri: /etc/ciao/configuration.yaml + controller: + compute_ca: /etc/pki/ciao/compute_ca.pem + compute_cert: /etc/pki/ciao/compute_key.pem + identity_user: controller + identity_password: ciao + launcher: + compute_net: 192.168.1.110 + mgmt_net: 192.168.1.111 + image_service: + url: http://glance.example.com + identity_service: + url: http://keystone.example.com +` +const fullValidConf = `configure: + scheduler: + storage_type: file + storage_uri: /etc/ciao/configuration.yaml + controller: + compute_port: 8774 + compute_ca: /etc/pki/ciao/compute_ca.pem + compute_cert: /etc/pki/ciao/compute_key.pem + identity_user: controller + identity_password: ciao + launcher: + compute_net: 192.168.1.110 + mgmt_net: 192.168.1.111 + disk_limit: true + mem_limit: true + image_service: + type: glance + url: http://glance.example.com + identity_service: + type: keystone + url: http://keystone.example.com +` + +func testBlob(t *testing.T, conf *payloads.Configure, expectedBlob []byte, positive bool) { + blob, err := Blob(conf) + if positive == true && err != nil { + t.Fatalf("%s", err) + } + if positive == false && err == nil { + t.Fatalf("%s", err) + } + if positive == true && expectedBlob != nil { + if bytes.Equal(expectedBlob, blob) == false { + t.Fatalf("expected %v got %v", expectedBlob, blob) + } + } +} + +func TestBlobEmptyPayload(t *testing.T) { + testBlob(t, &payloads.Configure{}, nil, false) +} + +func TestBlobCorrectPayload(t *testing.T) { + var payload payloads.Configure + fillPayload(&payload) + testBlob(t, &payload, []byte(fullValidConf), true) +} + +func equalPayload(p1, p2 payloads.Configure) bool { + return (p1.Configure.Scheduler.ConfigStorageType == p2.Configure.Scheduler.ConfigStorageType && + p1.Configure.Scheduler.ConfigStorageURI == p2.Configure.Scheduler.ConfigStorageURI && + + p1.Configure.Controller.ComputePort == p2.Configure.Controller.ComputePort && + p1.Configure.Controller.HTTPSCACert == p2.Configure.Controller.HTTPSCACert && + p1.Configure.Controller.HTTPSKey == p2.Configure.Controller.HTTPSKey && + p1.Configure.Controller.IdentityUser == p2.Configure.Controller.IdentityUser && + p1.Configure.Controller.IdentityPassword == p2.Configure.Controller.IdentityPassword && + + p1.Configure.Launcher.ComputeNetwork == p2.Configure.Launcher.ComputeNetwork && + p1.Configure.Launcher.ManagementNetwork == p2.Configure.Launcher.ManagementNetwork && + p1.Configure.Launcher.DiskLimit == p2.Configure.Launcher.DiskLimit && + p1.Configure.Launcher.MemoryLimit == p2.Configure.Launcher.MemoryLimit && + + p1.Configure.ImageService.Type == p2.Configure.ImageService.Type && + p1.Configure.ImageService.URL == p2.Configure.ImageService.URL && + + p1.Configure.IdentityService.Type == p2.Configure.IdentityService.Type && + p1.Configure.IdentityService.URL == p2.Configure.IdentityService.URL) +} + +func emptyPayload(p payloads.Configure) bool { + return (p.Configure.Scheduler.ConfigStorageType != "" && + p.Configure.Scheduler.ConfigStorageURI != "" && + + p.Configure.Controller.ComputePort != 0 && + p.Configure.Controller.HTTPSCACert != "" && + p.Configure.Controller.HTTPSKey != "" && + p.Configure.Controller.IdentityUser != "" && + p.Configure.Controller.IdentityPassword != "" && + + p.Configure.Launcher.ComputeNetwork != "" && + p.Configure.Launcher.ManagementNetwork != "" && + p.Configure.Launcher.DiskLimit != false && + p.Configure.Launcher.MemoryLimit != false && + + p.Configure.ImageService.Type != "" && + p.Configure.ImageService.URL != "" && + + p.Configure.IdentityService.Type != "" && + p.Configure.IdentityService.URL != "") +} + +func fillPayload(conf *payloads.Configure) { + fillDefaults(conf) + conf.Configure.Scheduler.ConfigStorageURI = storageURI + conf.Configure.Controller.HTTPSCACert = httpsCACert + conf.Configure.Controller.HTTPSKey = httpsKey + conf.Configure.Controller.IdentityUser = identityUser + conf.Configure.Controller.IdentityPassword = identityPassword + conf.Configure.Launcher.ComputeNetwork = computeNet + conf.Configure.Launcher.ManagementNetwork = mgmtNet + conf.Configure.ImageService.URL = glanceURL + conf.Configure.IdentityService.URL = keystoneURL +} + +func testPayload(t *testing.T, blob []byte, expectedConf payloads.Configure, positive bool) { + var conf payloads.Configure + err := Payload(blob, &conf) + + // expected FAIL + if positive == false && err == nil { + t.Fatalf("%s", err) + } + // expected PASS + if positive == true && err != nil { + t.Fatalf("%s", err) + } + if positive == true && emptyPayload(expectedConf) == false { + if equalPayload(expectedConf, conf) == false { + t.Fatalf("Expected %v got %v", expectedConf, conf) + } + } +} + +func TestPayloadNilBlob(t *testing.T) { + var conf payloads.Configure + testPayload(t, nil, conf, false) +} + +func TestPayloadFuzzyBlob(t *testing.T) { + var conf payloads.Configure + var fuzzyStr string + f := fuzz.New() + f.Fuzz(&fuzzyStr) + testPayload(t, []byte(fuzzyStr), conf, false) +} + +func TestPayloadCorrectBlob(t *testing.T) { + var expectedConf payloads.Configure + + fillPayload(&expectedConf) + testPayload(t, []byte(fullValidConf), expectedConf, true) +} + +func saneDefaults(conf *payloads.Configure) bool { + return (conf.Configure.Scheduler.ConfigStorageType == payloads.Filesystem && + conf.Configure.Controller.ComputePort == 8774 && + conf.Configure.ImageService.Type == payloads.Glance && + conf.Configure.IdentityService.Type == payloads.Keystone && + conf.Configure.Launcher.DiskLimit == true && + conf.Configure.Launcher.MemoryLimit == true) +} + +func TestFillDefaults(t *testing.T) { + var conf payloads.Configure + fillDefaults(&conf) + res := saneDefaults(&conf) + if res != true { + t.Fatalf("Expected true, got %v", res) + } +} + +func TestValidMinConf(t *testing.T) { + var conf payloads.Configure + + conf.Configure.Scheduler.ConfigStorageURI = storageURI + conf.Configure.Controller.HTTPSCACert = httpsCACert + conf.Configure.Controller.HTTPSKey = httpsKey + conf.Configure.Controller.IdentityUser = identityUser + conf.Configure.Controller.IdentityPassword = identityPassword + conf.Configure.Launcher.ComputeNetwork = computeNet + conf.Configure.Launcher.ManagementNetwork = mgmtNet + conf.Configure.ImageService.URL = glanceURL + + valid := validMinConf(&conf) + if valid != false { + t.Fatalf("Expected false, got %v", valid) + } + + // missing value to get minimal valid Configuration + conf.Configure.IdentityService.URL = keystoneURL + + valid = validMinConf(&conf) + if valid != true { + t.Fatalf("Expected true, got %v", valid) + } +} + +func testExtractBlob(t *testing.T, uri string, expectedBlob []byte, positive bool) { + blob, err := ExtractBlob(uri) + // expected FAIL + if positive == false && err == nil { + t.Fatalf("%s", err) + } + // expected PASS + if positive == true && err != nil { + t.Fatalf("%s", err) + } + if positive == true && expectedBlob != nil { + if bytes.Equal(expectedBlob, blob) == false { + t.Fatalf("Expected %v got %v", expectedBlob, blob) + } + } +} + +func TestExtractBlobInvalidURI(t *testing.T) { + testExtractBlob(t, invalidURI, nil, false) +} + +func TestExtractBlobEmptyPathURI(t *testing.T) { + testExtractBlob(t, emptyPathURI, nil, false) +} + +func TestExtractBlobBadSchemeURI(t *testing.T) { + testExtractBlob(t, badScheme, nil, false) +} + +func TestExtractBlobMalformedConf(t *testing.T) { + // create temp file where we can read the conf + tmpf, err := ioutil.TempFile("", "configuration.yaml") + if err != nil { + panic(err) + } + defer syscall.Unlink(tmpf.Name()) + ioutil.WriteFile(tmpf.Name(), []byte(malformedConf), 0644) + + testExtractBlob(t, "file://"+tmpf.Name(), nil, false) +} + +func TestExtractBlobValidConf(t *testing.T) { + // create temp file where we can read the conf + tmpf, err := ioutil.TempFile("", "configuration.yaml") + if err != nil { + panic(err) + } + defer syscall.Unlink(tmpf.Name()) + ioutil.WriteFile(tmpf.Name(), []byte(fullValidConf), 0644) + + testExtractBlob(t, "file://"+tmpf.Name(), []byte(fullValidConf), true) +} diff --git a/configuration/driver.go b/configuration/driver.go new file mode 100644 index 000000000..18c3770b4 --- /dev/null +++ b/configuration/driver.go @@ -0,0 +1,37 @@ +// +// Copyright (c) 2016 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package configuration + +import ( + "github.com/01org/ciao/payloads" +) + +type driver interface { + // fetchConfiguration should implement a backend + // which produces a correct payloads.Configure structure + // from a URI which will be handled by a specific + // driver, depending on the URI's scheme given. + // e.g: 'file' scheme is handled by the 'file' driver. + fetchConfiguration(uri string) (payloads.Configure, error) + + // storeConfiguration should implement a backend + // responsible to save the new configuration provided + // in a payloads.Configure structure. + // e.g: 'file' driver will save new configuration in a + // YAML file. + storeConfiguration(payloads.Configure) error +} diff --git a/configuration/etcd.go b/configuration/etcd.go new file mode 100644 index 000000000..24da1de92 --- /dev/null +++ b/configuration/etcd.go @@ -0,0 +1,36 @@ +// +// Copyright (c) 2016 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package configuration + +import ( + "errors" + + "github.com/01org/ciao/payloads" +) + +// TODO: implement etcd struct +type etcd struct { +} + +func (e *etcd) fetchConfiguration(uriStr string) (conf payloads.Configure, err error) { + return conf, errors.New("Not implemented") +} + +func (e *etcd) storeConfiguration(payloads.Configure) error { + //empty for now + return nil +} diff --git a/configuration/file.go b/configuration/file.go new file mode 100644 index 000000000..e2a8842d3 --- /dev/null +++ b/configuration/file.go @@ -0,0 +1,68 @@ +// +// Copyright (c) 2016 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package configuration + +import ( + "errors" + "io/ioutil" + "net/url" + + "github.com/01org/ciao/payloads" +) + +type file struct { + file string +} + +// handle the yaml configuration file read +// only 'file' URI Scheme is supported at the moment +// TODO: rework this more to check what's missing to +// support etcd backend as well +func loadFile(path string) (yamlConf []byte, err error) { + yamlConf, err = ioutil.ReadFile(path) + if err != nil { + return nil, err + } + return yamlConf, nil +} + +func (f *file) fetchConfiguration(uriStr string) (conf payloads.Configure, err error) { + uri, err := url.Parse(uriStr) + if err != nil { + return conf, err + } + if uri.Path == "" { + return conf, errors.New("configuration URI path is empty") + } + yamlConf, err := loadFile(uri.Path) + if err != nil { + return conf, err + } + fillDefaults(&conf) + err = Payload(yamlConf, &conf) + if err != nil { + return conf, err + } + // save configuration file URI + f.file = uriStr + return conf, nil +} + +func (f *file) storeConfiguration(payloads.Configure) error { + //empty for now + return nil +} diff --git a/configuration/file_test.go b/configuration/file_test.go new file mode 100644 index 000000000..525477b6d --- /dev/null +++ b/configuration/file_test.go @@ -0,0 +1,133 @@ +// +// Copyright (c) 2016 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package configuration + +import ( + "io/ioutil" + "syscall" + "testing" + + "github.com/01org/ciao/payloads" + "github.com/google/gofuzz" +) + +// missing the 'configure' identation +const malformedConf = `--- +scheduler: + storage_uri: /etc/ciao/configuration.yaml +controller: + compute_ca: /etc/pki/ciao/compute_ca.pem + compute_cert: /etc/pki/ciao/compute_key.pem + identity_user: controller + identity_password: ciao +launcher: + compute_net: 192.168.1.110 + mgmt_net: 192.168.1.111 +image_service: + url: http://glance.example.com +identity_service: + url: http://keystone.example.com +` + +func TestLoadFile(t *testing.T) { + var fuzzyStr string + f := fuzz.New() + f.Fuzz(&fuzzyStr) + + // create temp file where we can read the conf + tmpf, err := ioutil.TempFile("", "configuration.yaml") + if err != nil { + panic(err) + } + defer syscall.Unlink(tmpf.Name()) + ioutil.WriteFile(tmpf.Name(), []byte(minValidConf), 0644) + + _, err = loadFile(tmpf.Name()) + if err != nil { + t.Fatalf("expected nil, got %v", err) + } + + yamlConf, _ := loadFile(fuzzyStr) + if yamlConf != nil { + t.Fatalf("expected nil, got %v", yamlConf) + } + +} + +func testFileFetchConfiguration(t *testing.T, uri string, drv driver, + expectedPayload payloads.Configure, positive bool) { + payload, err := drv.fetchConfiguration(uri) + if positive == true && err != nil { + t.Fatalf("%s", err) + } + if positive == false && err == nil { + t.Fatalf("%s", err) + } + + if positive == true && emptyPayload(expectedPayload) == false { + if equalPayload(payload, expectedPayload) == false { + t.Fatalf("expected:%v got %v", + expectedPayload, payload) + } + } +} + +func TestFileFetchConfigurationFuzzyPath(t *testing.T) { + var fuzzyStr string + + f := fuzz.New() + f.Fuzz(&fuzzyStr) + // file doesn't exists + uri := emptyPathURI + "/" + fuzzyStr + testFileFetchConfiguration(t, uri, &file{}, payloads.Configure{}, false) +} + +func TestFileFetchConfigurationBadScheme(t *testing.T) { + // uri parse error due to "%z " on string + uri := "%z" + emptyPathURI + testFileFetchConfiguration(t, uri, &file{}, payloads.Configure{}, false) +} + +func TestFileFetchConfigurationCorrectURI(t *testing.T) { + var expectedPayload payloads.Configure + + fillPayload(&expectedPayload) + + // create temp file where we can read the conf + tmpf, err := ioutil.TempFile("", "configuration.yaml") + if err != nil { + panic(err) + } + defer syscall.Unlink(tmpf.Name()) + ioutil.WriteFile(tmpf.Name(), []byte(fullValidConf), 0644) + + uri := "file://" + tmpf.Name() + testFileFetchConfiguration(t, uri, &file{}, expectedPayload, true) +} + +func TestFileStoreConfiguration(t *testing.T) { + var d driver + var conf payloads.Configure + + fillDefaults(&conf) + d = &file{} + + err := d.storeConfiguration(conf) + if err != nil { + t.Errorf("expected nil, got %v", err) + } +} diff --git a/ssntp/server.go b/ssntp/server.go index 782bbae2c..2309b915c 100644 --- a/ssntp/server.go +++ b/ssntp/server.go @@ -24,6 +24,7 @@ import ( "sync" "time" + "github.com/01org/ciao/configuration" "github.com/docker/distribution/uuid" ) @@ -249,6 +250,14 @@ func (server *Server) Serve(config *Config, ntf ServerNotifier) error { transport := config.transport() uri = config.URI + payload, err := configuration.ExtractBlob(config.ConfigURI) + + if err != nil { + server.log.Errorf("Error loading configuration data from %s: %s - You may have not installed your configuration file yet", config.ConfigURI, err) + } else { + server.configuration.setConfiguration(payload) + } + server.ntf = ntf server.sessions = make(map[string]*session) server.forwardRules.init(config.ForwardRules) diff --git a/ssntp/ssntp.go b/ssntp/ssntp.go index 4fe31f04d..f3097ff27 100644 --- a/ssntp/ssntp.go +++ b/ssntp/ssntp.go @@ -778,6 +778,12 @@ type Config struct { // If Dial() succeeded and is connected to a server, nil will be // pushed to SyncChannel SyncChannel chan error + + // ConfigURI contains the location of the configuration that the + // SSNTP server will fetch to setup the cluster. + // Configuration driver (e.g: 'file' or 'etcd') will be determinated + // from the URI scheme. + ConfigURI string } // Logger is an interface for SSNTP users to define their own