// writeFiles persists the given files to the system. func (imp *Implementation) writeFiles(files []cloudconfig.WriteFile) { for _, f := range files { p, err := strconv.Atoi(f.Permissions) if err != nil { flog.Error("Failed to convert permissions", flog.Fields{ Event: "strconv.Atoi", Error: err, }, flog.Details{ "file": f.Path, "permissions": f.Permissions, }, ) continue } perms := os.FileMode(p) err = file.New(f.Path, file.Permissions(perms), file.Contents(f.Content)) if err != nil { flog.Error("Failed to create file", flog.Fields{ Event: "file.New", Error: err, }, flog.Details{ "file": f.Path, }, ) } } }
// ConsumeMetadata uses the given userdata to contextualize the distribution implementation. func (imp *Implementation) ConsumeMetadata(m *metadata.Digest) error { flog.Info("Consuming meta-data", flog.Fields{ Event: "distro.Implementation.ConsumeMetadata", }, ) if err := imp.setHostname(m.Hostname); err != nil { flog.Error("Failed to set hostname", flog.Fields{ Event: "distro.Implementation.setHostname", }, flog.Details{ "name": m.Hostname, }, ) return err } imp.consumeSSHKeys(m.SSHKeys) flog.Info("Finished consuming meta-data", flog.Fields{ Event: "distro.Implementation.ConsumeUserdata", }, ) return nil }
// consumeCommands executes the given command and it's arguments. func (imp *Implementation) consumeCommands(commands [][]string) { for _, cmd := range commands { flog.Debug("Executing command", flog.Fields{ Event: "distro.Implementation.consumeCommands", }, flog.Details{ "command": cmd, }, ) out, err := imp.Execute(cmd[0], cmd[1:]...) if err != nil { flog.Error("Failed to execute command", flog.Fields{ Event: "distro.Implementation.Execute", Error: err, }, flog.Details{ "command": cmd, }, ) } flog.Debug("Executed command", flog.Fields{ Event: "distro.Implementation.consumeCommands", }, flog.Details{ "command": cmd, "output": out, }, ) } }
// consumeScripts executes the given name/script content pairs. func (imp *Implementation) consumeScripts(scripts map[string]string) { for name, content := range scripts { flog.Info("Executing script", flog.Fields{ Event: "distro.consumeScripts", }, flog.Details{ "name": name, }, ) if err := imp.executeScript(content); err != nil { flog.Error("Failed to execute script", flog.Fields{ Event: "distro.executeScript", }, flog.Details{ "name": name, }, flog.DebugFields{ "content": content, }, ) } } }
// consumeSSHKeys authorizes the given SSH keys for the user. func (imp *Implementation) consumeSSHKeys(userKeys map[string][]ssh.Key) { for userName, sshKeys := range userKeys { flog.Info("Authorizing SSH keys", flog.Fields{ Event: "distro.consumeSSHKeys", }, flog.Details{ "user": userName, }, ) usr, err := nss.GetUser(userName) if err != nil { flog.Error("Failed to retrieve user NSS entry", flog.Fields{ Event: "nss.GetUser", Error: err, }, flog.Details{ "user": userName, }, ) continue } if err := ssh.AuthorizeKeysFor(usr, sshKeys); err != nil { flog.Error("Failed to authorize SSH keys for user", flog.Fields{ Event: "ssh.AuthorizeKeysFor", Error: err, }, flog.Details{ "user": userName, }, flog.DebugFields{ "SSHKeys": sshKeys, }, ) } } }
// consumeCloudConfig parses the given cloud config file contents and // consumes the parsed directives. func (imp *Implementation) consumeCloudConfig(contents string) error { conf, err := cloudconfig.Parse(strutil.ToReadCloser(contents)) if err != nil { flog.Error("Failed to parse cloud config file", flog.Fields{ Event: "cloudconfig.Parse", Error: err, }, ) return err } flog.Debug("Persisting files", flog.Fields{ Event: "distro.Implementation.consumeCloudConfig", }, ) imp.writeFiles(conf.Files) imp.consumeCommands(conf.Commands) for grpName, _ := range conf.Groups { flog.Info("Creating user group", flog.Fields{ Event: "distro.consumeCloudConfig", }, flog.Details{ "group": grpName, }, ) newGrp := identity.Group{ Name: grpName, } if err := imp.ID.CreateGroup(newGrp); err != nil { flog.Error("Failed to create a user group", flog.Fields{ Event: "identityManager.CreateGroup", Error: err, }, flog.Details{ "group": grpName, }, ) } } for _, usr := range conf.Users { flog.Info("Creating user", flog.Fields{ Event: "distro.consumeCloudConfig", }, flog.Details{ "user": usr.Name, }, ) if err := imp.ID.CreateUser(usr); err != nil { flog.Error("Failed to create a user", flog.Fields{ Event: "identityManager.CreateUser", Error: err, }, flog.Details{ "user": usr.Name, }, ) } } for grpName, usrNames := range conf.Groups { for _, usrName := range usrNames { flog.Info("Adding user to group", flog.Fields{ Event: "distro.consumeCloudConfig", }, flog.Details{ "user": usrName, "group": grpName, }, ) if err := imp.ID.AddUserToGroup(usrName, grpName); err != nil { flog.Error("Failed to add user to group", flog.Fields{ Event: "identityManager.AddUserToGroup", Error: err, }, flog.Details{ "user": usrName, "group": grpName, }, ) } } } imp.consumeSSHKeys(conf.AuthorizedKeys) return err }
// ConsumeUserdata uses the given userdata to contextualize the distribution implementation. func (imp *Implementation) ConsumeUserdata(u userdata.Map) error { // TODO(tmrts): Store unused user-data in files? // TODO(tmrts): Execute scripts in rc.local or a similar level flog.Info("Consuming user-data", flog.Fields{ Event: "distro.ConsumeUserdata", }, ) // TODO(tmrts): Use only scripts with 'startup', 'shutdown', 'user-data'. scripts := u.Scripts() flog.Info("Searched for script files", flog.Fields{ Event: "userdata.Map.Scripts", }, flog.Details{ "count": len(scripts), }, ) imp.consumeScripts(scripts) confs := u.CloudConfigs() flog.Info("Searched for cloud-config files", flog.Fields{ Event: "userdata.Map.CloudConfigs", }, flog.Details{ "count": len(confs), }, ) for name, content := range confs { flog.Info("Consuming user-data file", flog.Fields{ Event: "distro.ConsumeUserdata", }, flog.Details{ "name": name, }, ) err := imp.consumeCloudConfig(content) if err != nil { flog.Error("Failed to consume cloud-config file", flog.Fields{ Event: "distro.consumeCloudConfig", }, flog.Details{ "name": name, }, flog.DebugFields{ "content": content, }, ) return err } } flog.Info("Finished consuming user-data", flog.Fields{ Event: "distro.Implementation.ConsumeUserdata", }, ) return nil }