garm/vendor/github.com/cloudbase/garm-provider-common/cloudconfig/cloudconfig.go
Gabriel 23f92bc335
Add runner install template management (#525)
* Add template api endpoints

Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>

* Added template bypass

Pools and scale sets will automatically migrate to the new template
system for runner install scripts. If a pool or a scale set cannot be
migrate, it is left alone. It is expected that users set a runner install
template manually for scenarios we don't yet have a template for (windows
on gitea for example).

Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>

* Integrate templates with pool create/update

Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>

* Add webapp integration with templates

Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>

* Add unit tests

Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>

* Populate all relevant context fields

Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>

* Update dependencies

Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>

* Fix lint

Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>

* Validate uint

Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>

* Add CLI template management

Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>

* Some editor improvements and bugfixes

Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>

* Fix scale set return values post create

Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>

* Fix template websocket events filter

Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>

---------

Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
2025-09-23 13:46:27 +03:00

184 lines
4.1 KiB
Go

// Copyright 2022 Cloudbase Solutions SRL
//
// 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 cloudconfig
import (
"crypto/x509"
"encoding/base64"
"fmt"
"strings"
"sync"
"github.com/cloudbase/garm-provider-common/defaults"
"github.com/pkg/errors"
"gopkg.in/yaml.v3"
)
func NewDefaultCloudInitConfig() *CloudInit {
return &CloudInit{
PackageUpgrade: true,
Packages: []string{
"curl",
"tar",
},
Users: []string{"default"},
SystemInfo: &SystemInfo{
DefaultUser: DefaultUser{
Name: defaults.DefaultUser,
Home: fmt.Sprintf("/home/%s", defaults.DefaultUser),
Shell: defaults.DefaultUserShell,
Groups: defaults.DefaultUserGroups,
Sudo: "ALL=(ALL) NOPASSWD:ALL",
},
},
}
}
type DefaultUser struct {
Name string `yaml:"name"`
Home string `yaml:"home"`
Shell string `yaml:"shell"`
Groups []string `yaml:"groups,omitempty"`
Sudo string `yaml:"sudo"`
}
type SystemInfo struct {
DefaultUser DefaultUser `yaml:"default_user"`
}
type File struct {
Encoding string `yaml:"encoding"`
Content string `yaml:"content"`
Owner string `yaml:"owner"`
Path string `yaml:"path"`
Permissions string `yaml:"permissions"`
}
type CloudInit struct {
mux sync.Mutex
Users []string `yaml:"users"`
PackageUpgrade bool `yaml:"package_upgrade"`
Packages []string `yaml:"packages,omitempty"`
SSHAuthorizedKeys []string `yaml:"ssh_authorized_keys,omitempty"`
SystemInfo *SystemInfo `yaml:"system_info,omitempty"`
RunCmd []string `yaml:"runcmd,omitempty"`
WriteFiles []File `yaml:"write_files,omitempty"`
CACerts CACerts `yaml:"ca-certs,omitempty"`
}
type CACerts struct {
RemoveDefaults bool `yaml:"remove-defaults"`
Trusted []string `yaml:"trusted"`
}
func (c *CloudInit) AddCACert(cert []byte) error {
c.mux.Lock()
defer c.mux.Unlock()
if cert == nil {
return nil
}
roots := x509.NewCertPool()
if ok := roots.AppendCertsFromPEM(cert); !ok {
return fmt.Errorf("failed to parse CA cert bundle")
}
c.CACerts.Trusted = append(c.CACerts.Trusted, string(cert))
return nil
}
func (c *CloudInit) AddSSHKey(keys ...string) {
c.mux.Lock()
defer c.mux.Unlock()
// TODO(gabriel-samfira): Validate the SSH public key.
for _, key := range keys {
found := false
for _, val := range c.SSHAuthorizedKeys {
if val == key {
found = true
break
}
}
if !found {
c.SSHAuthorizedKeys = append(c.SSHAuthorizedKeys, key)
}
}
}
func (c *CloudInit) AddPackage(pkgs ...string) {
c.mux.Lock()
defer c.mux.Unlock()
for _, pkg := range pkgs {
found := false
for _, val := range c.Packages {
if val == pkg {
found = true
break
}
}
if !found {
c.Packages = append(c.Packages, pkg)
}
}
}
func (c *CloudInit) AddRunCmd(cmd string) {
c.mux.Lock()
defer c.mux.Unlock()
c.RunCmd = append(c.RunCmd, cmd)
}
func (c *CloudInit) AddFile(contents []byte, path, owner, permissions string) {
c.mux.Lock()
defer c.mux.Unlock()
for _, val := range c.WriteFiles {
if val.Path == path {
return
}
}
file := File{
Encoding: "b64",
Content: base64.StdEncoding.EncodeToString(contents),
Owner: owner,
Permissions: permissions,
Path: path,
}
c.WriteFiles = append(c.WriteFiles, file)
}
func (c *CloudInit) Serialize() (string, error) {
c.mux.Lock()
defer c.mux.Unlock()
ret := []string{
"#cloud-config",
}
asYaml, err := yaml.Marshal(c)
if err != nil {
return "", errors.Wrap(err, "marshaling to yaml")
}
ret = append(ret, string(asYaml))
return strings.Join(ret, "\n"), nil
}