示例#1
0
文件: disk.go 项目: jameinel/core
// Write implements EnvironInfo.Write.
func (info *environInfo) Write() error {
	data, err := goyaml.Marshal(info.EnvInfo)
	if err != nil {
		return errgo.Annotate(err, "cannot marshal environment info")
	}
	// Create a temporary file and rename it, so that the data
	// changes atomically.
	parent, _ := filepath.Split(info.path)
	tmpFile, err := ioutil.TempFile(parent, "")
	if err != nil {
		return errgo.Annotate(err, "cannot create temporary file")
	}
	_, err = tmpFile.Write(data)
	// N.B. We need to close the file before renaming it
	// otherwise it will fail under Windows with a file-in-use
	// error.
	tmpFile.Close()
	if err != nil {
		return errgo.Annotate(err, "cannot write temporary file")
	}
	if err := utils.ReplaceFile(tmpFile.Name(), info.path); err != nil {
		os.Remove(tmpFile.Name())
		return errgo.Annotate(err, "cannot rename new environment info file")
	}
	info.initialized = true
	return nil
}
示例#2
0
文件: charms.go 项目: jameinel/core
// processUploadedArchive opens the given charm archive from path,
// inspects it to see if it has all files at the root of the archive
// or it has subdirs. It repackages the archive so it has all the
// files at the root dir, if necessary, replacing the original archive
// at path.
func (h *charmsHandler) processUploadedArchive(path string) error {
	// Open the archive as a zip.
	f, err := os.OpenFile(path, os.O_RDWR, 0644)
	if err != nil {
		return err
	}
	defer f.Close()
	fi, err := f.Stat()
	if err != nil {
		return err
	}
	zipr, err := zip.NewReader(f, fi.Size())
	if err != nil {
		return errgo.Annotate(err, "cannot open charm archive")
	}

	// Find out the root dir prefix from the archive.
	rootDir, err := h.findArchiveRootDir(zipr)
	if err != nil {
		return errgo.Annotate(err, "cannot read charm archive")
	}
	if rootDir == "." {
		// Normal charm, just use charm.ReadBundle().
		return nil
	}

	// There is one or more subdirs, so we need extract it to a temp
	// dir and then read it as a charm dir.
	tempDir, err := ioutil.TempDir("", "charm-extract")
	if err != nil {
		return errgo.Annotate(err, "cannot create temp directory")
	}
	defer os.RemoveAll(tempDir)
	if err := ziputil.Extract(zipr, tempDir, rootDir); err != nil {
		return errgo.Annotate(err, "cannot extract charm archive")
	}
	dir, err := charm.ReadDir(tempDir)
	if err != nil {
		return errgo.Annotate(err, "cannot read extracted archive")
	}

	// Now repackage the dir as a bundle at the original path.
	if err := f.Truncate(0); err != nil {
		return err
	}
	if err := dir.BundleTo(f); err != nil {
		return err
	}
	return nil
}
示例#3
0
文件: worker.go 项目: jameinel/core
func (h *RsyslogConfigHandler) SetUp() (watcher.NotifyWatcher, error) {
	if h.mode == RsyslogModeAccumulate {
		if err := h.ensureCertificates(); err != nil {
			return nil, errgo.Annotate(err, "failed to write rsyslog certificates")
		}
	}
	return h.st.WatchForEnvironConfigChanges()
}
示例#4
0
// FinishMachineConfig sets fields on a MachineConfig that can be determined by
// inspecting a plain config.Config and the machine constraints at the last
// moment before bootstrapping. It assumes that the supplied Config comes from
// an environment that has passed through all the validation checks in the
// Bootstrap func, and that has set an agent-version (via finding the tools to,
// use for bootstrap, or otherwise).
// TODO(fwereade) This function is not meant to be "good" in any serious way:
// it is better that this functionality be collected in one place here than
// that it be spread out across 3 or 4 providers, but this is its only
// redeeming feature.
func FinishMachineConfig(mcfg *cloudinit.MachineConfig, cfg *config.Config, cons constraints.Value) (err error) {
	defer errors.Maskf(&err, "cannot complete machine configuration")

	if err := PopulateMachineConfig(
		mcfg,
		cfg.Type(),
		cfg.AuthorizedKeys(),
		cfg.SSLHostnameVerification(),
		cfg.ProxySettings(),
		cfg.AptProxySettings(),
	); err != nil {
		return err
	}

	// The following settings are only appropriate at bootstrap time. At the
	// moment, the only state server is the bootstrap node, but this
	// will probably change.
	if !mcfg.Bootstrap {
		return nil
	}
	if mcfg.APIInfo != nil || mcfg.StateInfo != nil {
		return fmt.Errorf("machine configuration already has api/state info")
	}
	caCert, hasCACert := cfg.CACert()
	if !hasCACert {
		return fmt.Errorf("environment configuration has no ca-cert")
	}
	password := cfg.AdminSecret()
	if password == "" {
		return fmt.Errorf("environment configuration has no admin-secret")
	}
	passwordHash := utils.UserPasswordHash(password, utils.CompatSalt)
	mcfg.APIInfo = &api.Info{Password: passwordHash, CACert: caCert}
	mcfg.StateInfo = &state.Info{Password: passwordHash, CACert: caCert}

	// These really are directly relevant to running a state server.
	cert, key, err := cfg.GenerateStateServerCertAndKey()
	if err != nil {
		return errgo.Annotate(err, "cannot generate state server certificate")
	}

	srvInfo := params.StateServingInfo{
		StatePort:      cfg.StatePort(),
		APIPort:        cfg.APIPort(),
		Cert:           string(cert),
		PrivateKey:     string(key),
		SystemIdentity: mcfg.SystemPrivateSSHKey,
	}
	mcfg.StateServingInfo = &srvInfo
	mcfg.Constraints = cons
	if mcfg.Config, err = BootstrapConfig(cfg); err != nil {
		return err
	}

	return nil
}
示例#5
0
文件: cert.go 项目: jameinel/core
// Verify verifies that the given server certificate is valid with
// respect to the given CA certificate at the given time.
func Verify(srvCertPEM, caCertPEM string, when time.Time) error {
	caCert, err := ParseCert(caCertPEM)
	if err != nil {
		return errgo.Annotate(err, "cannot parse CA certificate")
	}
	srvCert, err := ParseCert(srvCertPEM)
	if err != nil {
		return errgo.Annotate(err, "cannot parse server certificate")
	}
	pool := x509.NewCertPool()
	pool.AddCert(caCert)
	opts := x509.VerifyOptions{
		DNSName:     "anyServer",
		Roots:       pool,
		CurrentTime: when,
	}
	_, err = srvCert.Verify(opts)
	return err
}
示例#6
0
文件: open.go 项目: jameinel/core
// DestroyInfo destroys the configuration data for the named
// environment from the given store.
func DestroyInfo(envName string, store configstore.Storage) error {
	info, err := store.ReadInfo(envName)
	if err != nil {
		if errors.IsNotFound(err) {
			return nil
		}
		return err
	}
	if err := info.Destroy(); err != nil {
		return errgo.Annotate(err, "cannot destroy environment configuration information")
	}
	return nil
}
示例#7
0
文件: worker.go 项目: jameinel/core
func (h *RsyslogConfigHandler) Handle() error {
	cfg, err := h.st.EnvironConfig()
	if err != nil {
		return errgo.Annotate(err, "cannot get environ config")
	}
	rsyslogCACert := cfg.RsyslogCACert()
	if rsyslogCACert == "" {
		return nil
	}
	// If neither syslog-port nor rsyslog-ca-cert
	// have changed, we can drop out now.
	if cfg.SyslogPort() == h.syslogPort && rsyslogCACert == h.rsyslogCACert {
		return nil
	}
	h.syslogConfig.Port = cfg.SyslogPort()
	if h.mode == RsyslogModeForwarding {
		if err := writeFileAtomic(h.syslogConfig.CACertPath(), []byte(rsyslogCACert), 0644, 0, 0); err != nil {
			return errgo.Annotate(err, "cannot write CA certificate")
		}
	}
	data, err := h.syslogConfig.Render()
	if err != nil {
		return errgo.Annotate(err, "failed to render rsyslog configuration file")
	}
	if err := writeFileAtomic(h.syslogConfig.ConfigFilePath(), []byte(data), 0644, 0, 0); err != nil {
		return errgo.Annotate(err, "failed to write rsyslog configuration file")
	}
	logger.Debugf("Reloading rsyslog configuration")
	if err := restartRsyslog(); err != nil {
		logger.Errorf("failed to reload rsyslog configuration")
		return errgo.Annotate(err, "cannot restart rsyslog")
	}
	// Record config values so we don't try again.
	// Do this last so we recover from intermittent
	// failures.
	h.syslogPort = cfg.SyslogPort()
	h.rsyslogCACert = rsyslogCACert
	return nil
}
示例#8
0
// addAgentInfo adds agent-required information to the agent's directory
// and returns the agent directory name.
func (cfg *MachineConfig) addAgentInfo(c *cloudinit.Config, tag string) (agent.Config, error) {
	acfg, err := cfg.agentConfig(tag)
	if err != nil {
		return nil, err
	}
	acfg.SetValue(agent.AgentServiceName, cfg.MachineAgentServiceName)
	cmds, err := acfg.WriteCommands()
	if err != nil {
		return nil, errgo.Annotate(err, "failed to write commands")
	}
	c.AddScripts(cmds...)
	return acfg, nil
}
示例#9
0
// unitsChanged responds to changes to the assigned units.
func (fw *Firewaller) unitsChanged(change *unitsChange) error {
	changed := []*unitData{}
	for _, name := range change.units {
		unit, err := fw.st.Unit(names.UnitTag(name))
		if err != nil && !params.IsCodeNotFound(err) {
			return err
		}
		var machineTag string
		if unit != nil {
			machineTag, err = unit.AssignedMachine()
			if params.IsCodeNotFound(err) {
				continue
			} else if err != nil && !params.IsCodeNotAssigned(err) {
				return err
			}
		}
		if unitd, known := fw.unitds[name]; known {
			knownMachineTag := fw.unitds[name].machined.tag
			if unit == nil || unit.Life() == params.Dead || machineTag != knownMachineTag {
				fw.forgetUnit(unitd)
				changed = append(changed, unitd)
				logger.Debugf("stopped watching unit %s", name)
			}
		} else if unit != nil && unit.Life() != params.Dead && fw.machineds[machineTag] != nil {
			err = fw.startUnit(unit, machineTag)
			if err != nil {
				return err
			}
			changed = append(changed, fw.unitds[name])
			logger.Debugf("started watching unit %s", name)
		}
	}
	if err := fw.flushUnits(changed); err != nil {
		return errgo.Annotate(err, "cannot change firewall ports")
	}
	return nil
}
示例#10
0
// startMachine creates a new data value for tracking details of the
// machine and starts watching the machine for units added or removed.
func (fw *Firewaller) startMachine(tag string) error {
	machined := &machineData{
		fw:     fw,
		tag:    tag,
		unitds: make(map[string]*unitData),
		ports:  make([]instance.Port, 0),
	}
	m, err := machined.machine()
	if params.IsCodeNotFound(err) {
		return nil
	} else if err != nil {
		return errgo.Annotate(err, "cannot watch machine units")
	}
	unitw, err := m.WatchUnits()
	if err != nil {
		return err
	}
	select {
	case <-fw.tomb.Dying():
		stop("units watcher", unitw)
		return tomb.ErrDying
	case change, ok := <-unitw.Changes():
		if !ok {
			stop("units watcher", unitw)
			return watcher.MustErr(unitw)
		}
		fw.machineds[tag] = machined
		err = fw.unitsChanged(&unitsChange{machined, change})
		if err != nil {
			stop("units watcher", unitw)
			delete(fw.machineds, tag)
			return errgo.Annotatef(err, "cannot respond to units changes for %q", tag)
		}
	}
	go machined.watchLoop(unitw)
	return nil
}
示例#11
0
文件: charms.go 项目: jameinel/core
// downloadCharm downloads the given charm name from the provider storage and
// saves the corresponding zip archive to the given charmArchivePath.
func (h *charmsHandler) downloadCharm(name, charmArchivePath string) error {
	// Get the provider storage.
	storage, err := environs.GetStorage(h.state)
	if err != nil {
		return errgo.Annotate(err, "cannot access provider storage")
	}

	// Use the storage to retrieve and save the charm archive.
	reader, err := storage.Get(name)
	if err != nil {
		return errgo.Annotate(err, "charm not found in the provider storage")
	}
	defer reader.Close()
	data, err := ioutil.ReadAll(reader)
	if err != nil {
		return errgo.Annotate(err, "cannot read charm data")
	}
	// In order to avoid races, the archive is saved in a temporary file which
	// is then atomically renamed. The temporary file is created in the
	// charm cache directory so that we can safely assume the rename source and
	// target live in the same file system.
	cacheDir := filepath.Dir(charmArchivePath)
	if err = os.MkdirAll(cacheDir, 0755); err != nil {
		return errgo.Annotate(err, "cannot create the charms cache")
	}
	tempCharmArchive, err := ioutil.TempFile(cacheDir, "charm")
	if err != nil {
		return errgo.Annotate(err, "cannot create charm archive temp file")
	}
	defer tempCharmArchive.Close()
	if err = ioutil.WriteFile(tempCharmArchive.Name(), data, 0644); err != nil {
		return errgo.Annotate(err, "error processing charm archive download")
	}
	if err = os.Rename(tempCharmArchive.Name(), charmArchivePath); err != nil {
		defer os.Remove(tempCharmArchive.Name())
		return errgo.Annotate(err, "error renaming the charm archive")
	}
	return nil
}
示例#12
0
文件: client.go 项目: jameinel/core
// AddCharm adds the given charm URL (which must include revision) to
// the environment, if it does not exist yet. Local charms are not
// supported, only charm store URLs. See also AddLocalCharm().
func (c *Client) AddCharm(args params.CharmURL) error {
	charmURL, err := charm.ParseURL(args.URL)
	if err != nil {
		return err
	}
	if charmURL.Schema != "cs" {
		return fmt.Errorf("only charm store charm URLs are supported, with cs: schema")
	}
	if charmURL.Revision < 0 {
		return fmt.Errorf("charm URL must include revision")
	}

	// First, check if a pending or a real charm exists in state.
	stateCharm, err := c.api.state.PrepareStoreCharmUpload(charmURL)
	if err == nil && stateCharm.IsUploaded() {
		// Charm already in state (it was uploaded already).
		return nil
	} else if err != nil {
		return err
	}

	// Get the charm and its information from the store.
	envConfig, err := c.api.state.EnvironConfig()
	if err != nil {
		return err
	}
	store := config.SpecializeCharmRepo(CharmStore, envConfig)
	downloadedCharm, err := store.Get(charmURL)
	if err != nil {
		return errgo.Annotatef(err, "cannot download charm %q", charmURL.String())
	}

	// Open it and calculate the SHA256 hash.
	downloadedBundle, ok := downloadedCharm.(*charm.Bundle)
	if !ok {
		return errgo.New("expected a charm archive, got %T", downloadedCharm)
	}
	archive, err := os.Open(downloadedBundle.Path)
	if err != nil {
		return errgo.Annotate(err, "cannot read downloaded charm")
	}
	defer archive.Close()
	bundleSHA256, size, err := utils.ReadSHA256(archive)
	if err != nil {
		return errgo.Annotate(err, "cannot calculate SHA256 hash of charm")
	}
	if _, err := archive.Seek(0, 0); err != nil {
		return errgo.Annotate(err, "cannot rewind charm archive")
	}

	// Get the environment storage and upload the charm.
	env, err := environs.New(envConfig)
	if err != nil {
		return errgo.Annotate(err, "cannot access environment")
	}
	storage := env.Storage()
	archiveName, err := CharmArchiveName(charmURL.Name, charmURL.Revision)
	if err != nil {
		return errgo.Annotate(err, "cannot generate charm archive name")
	}
	if err := storage.Put(archiveName, archive, size); err != nil {
		return errgo.Annotate(err, "cannot upload charm to provider storage")
	}
	storageURL, err := storage.URL(archiveName)
	if err != nil {
		return errgo.Annotate(err, "cannot get storage URL for charm")
	}
	bundleURL, err := url.Parse(storageURL)
	if err != nil {
		return errgo.Annotate(err, "cannot parse storage URL")
	}

	// Finally, update the charm data in state and mark it as no longer pending.
	_, err = c.api.state.UpdateUploadedCharm(downloadedCharm, charmURL, bundleURL, bundleSHA256)
	if err == state.ErrCharmRevisionAlreadyModified ||
		state.IsCharmAlreadyUploadedError(err) {
		// This is not an error, it just signifies somebody else
		// managed to upload and update the charm in state before
		// us. This means we have to delete what we just uploaded
		// to storage.
		if err := storage.Remove(archiveName); err != nil {
			errgo.Annotate(err, "cannot remove duplicated charm from storage")
		}
		return nil
	}
	return err
}
示例#13
0
文件: config.go 项目: jameinel/core
// Validate ensures that config is a valid configuration.  If old is not nil,
// it holds the previous environment configuration for consideration when
// validating changes.
func Validate(cfg, old *Config) error {
	// Check that we don't have any disallowed fields.
	for _, attr := range allowedWithDefaultsOnly {
		if _, ok := cfg.defined[attr]; ok {
			return fmt.Errorf("attribute %q is not allowed in configuration", attr)
		}
	}
	// Check that mandatory fields are specified.
	for _, attr := range mandatoryWithoutDefaults {
		if _, ok := cfg.defined[attr]; !ok {
			return fmt.Errorf("%s missing from environment configuration", attr)
		}
	}

	// Check that all other fields that have been specified are non-empty,
	// unless they're allowed to be empty for backward compatibility,
	for attr, val := range cfg.defined {
		if !isEmpty(val) {
			continue
		}
		if !allowEmpty(attr) {
			return fmt.Errorf("empty %s in environment configuration", attr)
		}
	}

	if strings.ContainsAny(cfg.mustString("name"), "/\\") {
		return fmt.Errorf("environment name contains unsafe characters")
	}

	// Check that the agent version parses ok if set explicitly; otherwise leave
	// it alone.
	if v, ok := cfg.defined["agent-version"].(string); ok {
		if _, err := version.Parse(v); err != nil {
			return fmt.Errorf("invalid agent version in environment configuration: %q", v)
		}
	}

	// If the logging config is set, make sure it is valid.
	if v, ok := cfg.defined["logging-config"].(string); ok {
		if _, err := loggo.ParseConfigurationString(v); err != nil {
			return err
		}
	}

	// Check firewall mode.
	if mode := cfg.FirewallMode(); mode != FwInstance && mode != FwGlobal {
		return fmt.Errorf("invalid firewall mode in environment configuration: %q", mode)
	}

	caCert, caCertOK := cfg.CACert()
	caKey, caKeyOK := cfg.CAPrivateKey()
	if caCertOK || caKeyOK {
		if err := verifyKeyPair(caCert, caKey); err != nil {
			return errgo.Annotate(err, "bad CA certificate/key in configuration")
		}
	}

	// Ensure that the auth token is a set of key=value pairs.
	authToken, _ := cfg.CharmStoreAuth()
	validAuthToken := regexp.MustCompile(`^([^\s=]+=[^\s=]+(,\s*)?)*$`)
	if !validAuthToken.MatchString(authToken) {
		return fmt.Errorf("charm store auth token needs to be a set"+
			" of key-value pairs, not %q", authToken)
	}

	// Check the immutable config values.  These can't change
	if old != nil {
		for _, attr := range immutableAttributes {
			if newv, oldv := cfg.defined[attr], old.defined[attr]; newv != oldv {
				return fmt.Errorf("cannot change %s from %#v to %#v", attr, oldv, newv)
			}
		}
		if _, oldFound := old.AgentVersion(); oldFound {
			if _, newFound := cfg.AgentVersion(); !newFound {
				return fmt.Errorf("cannot clear agent-version")
			}
		}
	}

	cfg.processDeprecatedAttributes()
	return nil
}
示例#14
0
func (fw *Firewaller) loop() error {
	defer fw.stopWatchers()

	var err error
	var reconciled bool

	fw.environ, err = worker.WaitForEnviron(fw.environWatcher, fw.st, fw.tomb.Dying())
	if err != nil {
		return err
	}
	if fw.environ.Config().FirewallMode() == config.FwGlobal {
		fw.globalMode = true
		fw.globalPortRef = make(map[instance.Port]int)
	}
	for {
		select {
		case <-fw.tomb.Dying():
			return tomb.ErrDying
		case _, ok := <-fw.environWatcher.Changes():
			if !ok {
				return watcher.MustErr(fw.environWatcher)
			}
			config, err := fw.st.EnvironConfig()
			if err != nil {
				return err
			}
			if err := fw.environ.SetConfig(config); err != nil {
				logger.Errorf("loaded invalid environment configuration: %v", err)
			}
		case change, ok := <-fw.machinesWatcher.Changes():
			if !ok {
				return watcher.MustErr(fw.machinesWatcher)
			}
			for _, machineId := range change {
				fw.machineLifeChanged(names.MachineTag(machineId))
			}
			if !reconciled {
				reconciled = true
				var err error
				if fw.globalMode {
					err = fw.reconcileGlobal()
				} else {
					err = fw.reconcileInstances()
				}
				if err != nil {
					return err
				}
			}
		case change := <-fw.unitsChange:
			if err := fw.unitsChanged(change); err != nil {
				return err
			}
		case change := <-fw.portsChange:
			change.unitd.ports = change.ports
			if err := fw.flushUnits([]*unitData{change.unitd}); err != nil {
				return errgo.Annotate(err, "cannot change firewall ports")
			}
		case change := <-fw.exposedChange:
			change.serviced.exposed = change.exposed
			unitds := []*unitData{}
			for _, unitd := range change.serviced.unitds {
				unitds = append(unitds, unitd)
			}
			if err := fw.flushUnits(unitds); err != nil {
				return errgo.Annotate(err, "cannot change firewall ports")
			}
		}
	}
}
示例#15
0
文件: charms.go 项目: jameinel/core
// repackageAndUploadCharm expands the given charm archive to a
// temporary directoy, repackages it with the given curl's revision,
// then uploads it to providr storage, and finally updates the state.
func (h *charmsHandler) repackageAndUploadCharm(archive *charm.Bundle, curl *charm.URL) error {
	// Create a temp dir to contain the extracted charm
	// dir and the repackaged archive.
	tempDir, err := ioutil.TempDir("", "charm-download")
	if err != nil {
		return errgo.Annotate(err, "cannot create temp directory")
	}
	defer os.RemoveAll(tempDir)
	extractPath := filepath.Join(tempDir, "extracted")
	repackagedPath := filepath.Join(tempDir, "repackaged.zip")
	repackagedArchive, err := os.Create(repackagedPath)
	if err != nil {
		return errgo.Annotate(err, "cannot repackage uploaded charm")
	}
	defer repackagedArchive.Close()

	// Expand and repack it with the revision specified by curl.
	archive.SetRevision(curl.Revision)
	if err := archive.ExpandTo(extractPath); err != nil {
		return errgo.Annotate(err, "cannot extract uploaded charm")
	}
	charmDir, err := charm.ReadDir(extractPath)
	if err != nil {
		return errgo.Annotate(err, "cannot read extracted charm")
	}

	// Bundle the charm and calculate its sha256 hash at the
	// same time.
	hash := sha256.New()
	err = charmDir.BundleTo(io.MultiWriter(hash, repackagedArchive))
	if err != nil {
		return errgo.Annotate(err, "cannot repackage uploaded charm")
	}
	bundleSHA256 := hex.EncodeToString(hash.Sum(nil))
	size, err := repackagedArchive.Seek(0, 2)
	if err != nil {
		return errgo.Annotate(err, "cannot get charm file size")
	}

	// Now upload to provider storage.
	if _, err := repackagedArchive.Seek(0, 0); err != nil {
		return errgo.Annotate(err, "cannot rewind the charm file reader")
	}
	storage, err := environs.GetStorage(h.state)
	if err != nil {
		return errgo.Annotate(err, "cannot access provider storage")
	}
	name := charm.Quote(curl.String())
	if err := storage.Put(name, repackagedArchive, size); err != nil {
		return errgo.Annotate(err, "cannot upload charm to provider storage")
	}
	storageURL, err := storage.URL(name)
	if err != nil {
		return errgo.Annotate(err, "cannot get storage URL for charm")
	}
	bundleURL, err := url.Parse(storageURL)
	if err != nil {
		return errgo.Annotate(err, "cannot parse storage URL")
	}

	// And finally, update state.
	_, err = h.state.UpdateUploadedCharm(archive, curl, bundleURL, bundleSHA256)
	if err != nil {
		return errgo.Annotate(err, "cannot update uploaded charm in state")
	}
	return nil
}