mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-06-20 22:26:09 +08:00
229 lines
4.9 KiB
Go
229 lines
4.9 KiB
Go
package cloudinit
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"yunion.io/x/jsonutils"
|
|
"yunion.io/x/log"
|
|
"yunion.io/x/pkg/utils"
|
|
|
|
"yunion.io/x/onecloud/pkg/util/seclib2"
|
|
)
|
|
|
|
/*
|
|
* cloudconfig
|
|
* Reference: https://cloudinit.readthedocs.io/en/latest/topics/examples.html
|
|
*
|
|
*/
|
|
|
|
type TSudoPolicy string
|
|
|
|
const (
|
|
CLOUD_CONFIG_HEADER = "#cloud-config\n"
|
|
|
|
USER_SUDO_NOPASSWD = TSudoPolicy("sudo_nopasswd")
|
|
USER_SUDO = TSudoPolicy("sudo")
|
|
USER_SUDO_DENY = TSudoPolicy("sudo_deny")
|
|
USER_SUDO_NONE = TSudoPolicy("")
|
|
)
|
|
|
|
type SWriteFile struct {
|
|
Path string
|
|
Permissions string
|
|
Owner string
|
|
Encoding string
|
|
Content string
|
|
}
|
|
|
|
type SUser struct {
|
|
Name string
|
|
Passwd string
|
|
LockPassword string
|
|
SshAuthorizedKeys []string
|
|
Sudo string
|
|
}
|
|
|
|
type SPhoneHome struct {
|
|
Url string
|
|
}
|
|
|
|
type SCloudConfig struct {
|
|
Users []SUser
|
|
WriteFiles []SWriteFile
|
|
Runcmd []string
|
|
Bootcmd []string
|
|
Packages []string
|
|
PhoneHome *SPhoneHome
|
|
DisableRoot int
|
|
SshPwauth int
|
|
}
|
|
|
|
func NewWriteFile(path string, content string, perm string, owner string, isBase64 bool) SWriteFile {
|
|
f := SWriteFile{}
|
|
|
|
f.Path = path
|
|
f.Permissions = perm
|
|
f.Owner = owner
|
|
if isBase64 {
|
|
f.Encoding = "b64"
|
|
f.Content = base64.StdEncoding.EncodeToString([]byte(content))
|
|
} else {
|
|
f.Content = content
|
|
}
|
|
|
|
return f
|
|
}
|
|
|
|
func NewUser(name string) SUser {
|
|
u := SUser{Name: name}
|
|
return u
|
|
}
|
|
|
|
func (u *SUser) SudoPolicy(policy TSudoPolicy) *SUser {
|
|
switch policy {
|
|
case USER_SUDO_NOPASSWD:
|
|
u.Sudo = "ALL=(ALL) NOPASSWD:ALL"
|
|
case USER_SUDO:
|
|
u.Sudo = "ALL=(ALL) ALL"
|
|
case USER_SUDO_DENY:
|
|
u.Sudo = "False"
|
|
default:
|
|
u.Sudo = ""
|
|
}
|
|
return u
|
|
}
|
|
|
|
func (u *SUser) SshKey(key string) *SUser {
|
|
if u.SshAuthorizedKeys == nil {
|
|
u.SshAuthorizedKeys = make([]string, 0)
|
|
}
|
|
u.SshAuthorizedKeys = append(u.SshAuthorizedKeys, key)
|
|
return u
|
|
}
|
|
|
|
func (u *SUser) Password(passwd string) *SUser {
|
|
if len(passwd) > 0 {
|
|
hash, err := seclib2.GeneratePassword(passwd)
|
|
if err != nil {
|
|
log.Errorf("GeneratePassword error %s", err)
|
|
} else {
|
|
u.Passwd = hash
|
|
}
|
|
u.LockPassword = "false"
|
|
}
|
|
return u
|
|
}
|
|
|
|
func (conf *SCloudConfig) UserData() string {
|
|
var buf bytes.Buffer
|
|
jsonConf := jsonutils.Marshal(conf)
|
|
buf.WriteString(CLOUD_CONFIG_HEADER)
|
|
buf.WriteString(jsonConf.YAMLString())
|
|
return buf.String()
|
|
}
|
|
|
|
func (conf *SCloudConfig) UserDataBase64() string {
|
|
data := conf.UserData()
|
|
return base64.StdEncoding.EncodeToString([]byte(data))
|
|
}
|
|
|
|
func ParseUserDataBase64(b64data string) (*SCloudConfig, error) {
|
|
data, err := base64.StdEncoding.DecodeString(b64data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return ParseUserData(string(data))
|
|
}
|
|
|
|
func ParseUserData(data string) (*SCloudConfig, error) {
|
|
if !strings.HasPrefix(data, CLOUD_CONFIG_HEADER) {
|
|
msg := "invalid userdata, not starting with #cloud-config"
|
|
log.Errorf(msg)
|
|
return nil, fmt.Errorf(msg)
|
|
}
|
|
jsonConf, err := jsonutils.ParseYAML(data)
|
|
if err != nil {
|
|
log.Errorf("parse userdata yaml error %s", err)
|
|
return nil, err
|
|
}
|
|
config := SCloudConfig{}
|
|
err = jsonConf.Unmarshal(&config)
|
|
if err != nil {
|
|
log.Errorf("unable to unmarchal userdata %s", err)
|
|
return nil, err
|
|
}
|
|
return &config, nil
|
|
}
|
|
|
|
func (conf *SCloudConfig) MergeUser(u SUser) {
|
|
for i := 0; i < len(conf.Users); i += 1 {
|
|
if u.Name == conf.Users[i].Name {
|
|
// find user, merge keys
|
|
for j := 0; j < len(u.SshAuthorizedKeys); j += 1 {
|
|
if !utils.IsInStringArray(u.SshAuthorizedKeys[j], conf.Users[i].SshAuthorizedKeys) {
|
|
conf.Users[i].SshAuthorizedKeys = append(conf.Users[i].SshAuthorizedKeys, u.SshAuthorizedKeys[j])
|
|
}
|
|
}
|
|
return
|
|
}
|
|
}
|
|
// no such user
|
|
conf.Users = append(conf.Users, u)
|
|
}
|
|
|
|
func (conf *SCloudConfig) MergeWriteFile(f SWriteFile, replace bool) {
|
|
for i := 0; i < len(conf.WriteFiles); i += 1 {
|
|
if conf.WriteFiles[i].Path == f.Path {
|
|
// find file
|
|
if replace {
|
|
conf.WriteFiles[i].Content = f.Content
|
|
conf.WriteFiles[i].Encoding = f.Encoding
|
|
conf.WriteFiles[i].Owner = f.Owner
|
|
conf.WriteFiles[i].Permissions = f.Permissions
|
|
}
|
|
return
|
|
}
|
|
}
|
|
// no such file
|
|
conf.WriteFiles = append(conf.WriteFiles, f)
|
|
}
|
|
|
|
func (conf *SCloudConfig) MergeRuncmd(cmd string) {
|
|
if !utils.IsInStringArray(cmd, conf.Runcmd) {
|
|
conf.Runcmd = append(conf.Runcmd, cmd)
|
|
}
|
|
}
|
|
|
|
func (conf *SCloudConfig) MergeBootcmd(cmd string) {
|
|
if !utils.IsInStringArray(cmd, conf.Bootcmd) {
|
|
conf.Bootcmd = append(conf.Bootcmd, cmd)
|
|
}
|
|
}
|
|
|
|
func (conf *SCloudConfig) MergePackage(pkg string) {
|
|
if !utils.IsInStringArray(pkg, conf.Packages) {
|
|
conf.Packages = append(conf.Packages, pkg)
|
|
}
|
|
}
|
|
|
|
func (conf *SCloudConfig) Merge(conf2 *SCloudConfig) {
|
|
for _, u := range conf2.Users {
|
|
conf.MergeUser(u)
|
|
}
|
|
for _, f := range conf2.WriteFiles {
|
|
conf.MergeWriteFile(f, false)
|
|
}
|
|
for _, c := range conf2.Runcmd {
|
|
conf.MergeRuncmd(c)
|
|
}
|
|
for _, c := range conf2.Bootcmd {
|
|
conf.MergeBootcmd(c)
|
|
}
|
|
for _, p := range conf2.Packages {
|
|
conf.MergePackage(p)
|
|
}
|
|
}
|