Skip to content

Commit

Permalink
⚙️ Add c3os bundles
Browse files Browse the repository at this point in the history
  • Loading branch information
mudler authored Jul 14, 2022
1 parent 5f44c14 commit ea88699
Show file tree
Hide file tree
Showing 11 changed files with 385 additions and 15 deletions.
24 changes: 19 additions & 5 deletions Earthfile
Original file line number Diff line number Diff line change
Expand Up @@ -114,16 +114,26 @@ framework:
SAVE ARTIFACT /framework/ framework

docker:
# Source the flavor-provided docker file
FROM DOCKERFILE -f images/Dockerfile.$FLAVOR .
ARG K3S_VERSION
IF [ "$BASE_IMAGE" = "" ]
# Source the flavor-provided docker file
FROM DOCKERFILE -f images/Dockerfile.$FLAVOR .
ELSE
FROM $BASE_IMAGE
END
ARG C3OS_VERSION
ARG OS_VERSION=${K3S_VERSION}+k3s1-c3OS${C3OS_VERSION}
IF [ "$K3S_VERSION" = "" ]
ARG OS_VERSION=c3OS${C3OS_VERSION}
ELSE
ARG OS_VERSION=${K3S_VERSION}+k3s1-c3OS${C3OS_VERSION}
END
ARG OS_ID=c3os
ARG FLAVOR
ARG OS_NAME=${OS_ID}-${FLAVOR}
ARG OS_REPO=quay.io/c3os/c3os
ARG OS_LABEL=${FLAVOR}-latest
ARG WITH_PROVIDER=true
ARG WITH_CLI=true
ENV OS_LABEL=$OS_LABEL
ENV OS_NAME=$OS_NAME
ENV OS_ID=$OS_ID
Expand All @@ -144,9 +154,13 @@ docker:
END

# Copy c3os binaries
COPY +build-c3os-cli/c3os /usr/bin/c3os
COPY +build-c3os-agent/c3os-agent /usr/bin/c3os-agent
COPY +build-c3os-agent-provider/agent-provider-c3os /usr/bin/agent-provider-c3os
IF [ "$WITH_CLI" = "true" ]
COPY +build-c3os-cli/c3os /usr/bin/c3os
END
IF [ "$WITH_PROVIDER" = "true" ]
COPY +build-c3os-agent-provider/agent-provider-c3os /usr/bin/agent-provider-c3os
END

# update OS-release file
RUN envsubst >/etc/os-release </usr/lib/os-release.tmpl && \
Expand Down
11 changes: 11 additions & 0 deletions cmd/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"syscall"

"github.com/c3os-io/c3os/internal/bus"
machine "github.com/c3os-io/c3os/internal/machine"
"github.com/c3os-io/c3os/internal/utils"
events "github.com/c3os-io/c3os/pkg/bus"
config "github.com/c3os-io/c3os/pkg/config"
Expand Down Expand Up @@ -53,6 +54,16 @@ func agent(apiAddress string, dir []string, force bool) error {
}
}()

if !machine.SentinelExist("bundles") {
opts := c.Bundles.Options()
err := machine.RunBundles(opts...)
if !c.IgnoreBundleErrors && err != nil {
return err
}

machine.CreateSentinel("bundles")
}

_, err = bus.Manager.Publish(events.EventBootstrap, events.BootstrapPayload{APIAddress: apiAddress, Config: c.String(), Logfile: f.Name()})
return err
}
27 changes: 27 additions & 0 deletions cmd/agent/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,33 @@ Starts the c3os agent which automatically bootstrap and advertize to the c3os ne
return agent(c.String("api"), dirs, c.Bool("force"))
},
},
{
Name: "install-bundle",
Usage: "Installs a c3os bundle",
Description: `
Manually installs a c3os bundle.
E.g. c3os-agent install-bundle container:quay.io/c3os/c3os...
`,
Aliases: []string{"i"},
Flags: []cli.Flag{
&cli.StringFlag{
Name: "repository",
EnvVar: "REPOSITORY",
},
},
UsageText: "Install a bundle manually in the node",
Action: func(c *cli.Context) error {
args := c.Args()
if len(args) != 1 {
return fmt.Errorf("bundle name required")
}

return machine.RunBundles([]machine.BundleOption{machine.WithRepository(c.String("repository")), machine.WithTarget(args[0])})
},
},
{
Name: "rotate",
Usage: "Rotate a c3os node network configuration via CLI",
Expand Down
220 changes: 220 additions & 0 deletions internal/machine/bundles.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
package machine

import (
"fmt"
"io/ioutil"
"os"
"strings"

"github.com/c3os-io/c3os/internal/utils"
"github.com/hashicorp/go-multierror"
)

type BundleConfig struct {
Target string
Repository string
DBPath string
RootPath string
}

// bundles needs to ship only /config/... (etc) and /bin/... (will go to /usr/local/bin)
type BundleOption func(bc *BundleConfig) error

func (bc *BundleConfig) Apply(opts ...BundleOption) error {
for _, o := range opts {
if err := o(bc); err != nil {
return err
}
}
return nil
}

func WithDBHPath(r string) BundleOption {
return func(bc *BundleConfig) error {
bc.DBPath = r
return nil
}
}

func WithRootFS(r string) BundleOption {
return func(bc *BundleConfig) error {
bc.RootPath = r
return nil
}
}

func WithRepository(r string) BundleOption {
return func(bc *BundleConfig) error {
bc.Repository = r
return nil
}
}

func WithTarget(p string) BundleOption {
return func(bc *BundleConfig) error {
bc.Target = p
return nil
}
}

func (bc *BundleConfig) extractRepo() (string, string) {
s := strings.Split(bc.Repository, ":")
return s[0], s[1]
}

// XXX: directly to rootfs ? or maybe better to /usr/local/.c3os/rootfs and handle symlinks?
func defaultConfig() *BundleConfig {
return &BundleConfig{
DBPath: "/usr/local/.c3os/db",
RootPath: "/",
Repository: "quay.io/c3os/packages",
}
}

type BundleInstaller interface {
Install(*BundleConfig) error
}

// TODO:
// - Make provider consume bundles when bins are not detected in the rootfs
// - Default bundles preset in case of no binaries detected and version specified via config.

func RunBundles(bundles ...[]BundleOption) error {
var resErr error
for _, b := range bundles {
config := defaultConfig()
config.Apply(b...)

installer, err := NewBundleInstaller(*config)
if err != nil {
resErr = multierror.Append(err)
continue
}
dat := strings.Split(config.Target, ":")
if len(dat) != 2 {
resErr = multierror.Append(fmt.Errorf("invalid target"))

continue
}
config.Target = dat[1]

err = installer.Install(config)
if err != nil {
resErr = multierror.Append(err)
continue
}
}

return resErr
}

func NewBundleInstaller(bc BundleConfig) (BundleInstaller, error) {

dat := strings.Split(bc.Target, ":")
if len(dat) != 2 {
return nil, fmt.Errorf("could not decode scheme")
}
switch strings.ToLower(dat[0]) {
case "container":
return &ContainerInstaller{}, nil
case "run":
return &ContainerRunner{}, nil
case "package":
return &LuetInstaller{}, nil

}

return &LuetInstaller{}, nil
}

// BundleInstall installs a bundle from a luet repo or a container image
type ContainerRunner struct{}

func (l *ContainerRunner) Install(config *BundleConfig) error {

tempDir, err := ioutil.TempDir("", "containerrunner")
if err != nil {
return err
}
defer os.RemoveAll(tempDir)

out, err := utils.SH(
fmt.Sprintf(
`luet util unpack %s %s`,
config.Target,
tempDir,
),
)
if err != nil {
return fmt.Errorf("could not unpack container: %w - %s", err, out)
}

out, err = utils.SH(fmt.Sprintf("CONTAINERDIR=%s %s/run.sh", tempDir, tempDir))
if err != nil {
return fmt.Errorf("could not execute container: %w - %s", err, out)
}
return nil
}

type ContainerInstaller struct{}

func (l *ContainerInstaller) Install(config *BundleConfig) error {

//mkdir -p test/etc/luet/repos.conf.d
_, err := utils.SH(
fmt.Sprintf(
`luet util unpack %s %s`,
config.Target,
config.RootPath,
),
)
if err != nil {
return fmt.Errorf("could not add repository: %w", err)
}

return nil
}

type LuetInstaller struct{}

func (l *LuetInstaller) Install(config *BundleConfig) error {

t, repo := config.extractRepo()
//mkdir -p test/etc/luet/repos.conf.d
_, err := utils.SH(
fmt.Sprintf(
`LUET_CONFIG_FROM_HOST=false luet repo add --system-dbpath %s --system-target %s c3os-system -y --description "Automatically generated c3os-system" --url "%s" --type "%s"`,
config.DBPath,
config.RootPath,
repo,
t,
),
)
if err != nil {
return fmt.Errorf("could not add repository: %w", err)
}
_, err = utils.SH(
fmt.Sprintf(
`LUET_CONFIG_FROM_HOST=false luet repo update -f --system-dbpath %s --system-target %s`,
config.DBPath,
config.RootPath,
),
)
if err != nil {
return fmt.Errorf("could not sync repository: %w", err)
}
_, err = utils.SH(
fmt.Sprintf(
`LUET_CONFIG_FROM_HOST=false luet install -y --system-dbpath %s --system-target %s %s`,
config.DBPath,
config.RootPath,
config.Target,
),
)
if err != nil {
return fmt.Errorf("could not sync repository: %w", err)
}

// copy bins to /usr/local/bin
return nil
}
12 changes: 12 additions & 0 deletions internal/machine/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package machine

import (
"fmt"
"io/ioutil"
"os"

"github.com/c3os-io/c3os/internal/machine/openrc"
Expand Down Expand Up @@ -101,3 +102,14 @@ func UUID() string {
hostname, _ := os.Hostname()
return fmt.Sprintf("%s-%s", id, hostname)
}

func CreateSentinel(f string) error {
return ioutil.WriteFile(fmt.Sprintf("/usr/local/.c3os/sentinel_%s", f), []byte{}, os.ModePerm)
}

func SentinelExist(f string) bool {
if _, err := os.Stat(fmt.Sprintf("/usr/local/.c3os/sentinel_%s", f)); err == nil {
return true
}
return false
}
6 changes: 5 additions & 1 deletion internal/machine/openrc/unit.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,11 @@ func (s ServiceUnit) WriteUnit() error {

// TODO: This is too much k3s specific
func (s ServiceUnit) OverrideCmd(cmd string) error {
cmd = strings.ReplaceAll(cmd, "/usr/bin/k3s ", "")
k3sbin := utils.K3sBin()
if k3sbin == "" {
return fmt.Errorf("no k3s binary found (?)")
}
cmd = strings.ReplaceAll(cmd, k3sbin+" ", "")
svcDir := filepath.Join(s.rootdir, fmt.Sprintf("/etc/rancher/k3s/%s.env", s.name))

return ioutil.WriteFile(svcDir, []byte(fmt.Sprintf(`command_args="%s >>/var/log/%s.log 2>&1"`, cmd, s.name)), 0600)
Expand Down
7 changes: 6 additions & 1 deletion internal/provider/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ func Bootstrap(e *pluggable.Event) pluggable.EventResponse {
return pluggable.EventResponse{Error: fmt.Sprintf("Failed reading JSON input: %s input '%s'", err.Error(), cfg.Config)}
}

// TODO: this belong to a systemd service that is started instead
utils.SH("sysctl -w net.core.rmem_max=2500000")

tokenNotDefined := (c.C3OS == nil || c.C3OS.NetworkToken == "")
Expand Down Expand Up @@ -188,7 +189,11 @@ func oneTimeBootstrap(l logging.StandardLogger, c *config.Config, vpnSetupFN fun
return err
}

if err := svc.OverrideCmd(fmt.Sprintf("/usr/bin/k3s %s %s", svcRole, strings.Join(k3sConfig.Args, " "))); err != nil {
k3sbin := utils.K3sBin()
if k3sbin == "" {
return fmt.Errorf("no k3s binary found (?)")
}
if err := svc.OverrideCmd(fmt.Sprintf("%s %s %s", k3sbin, svcRole, strings.Join(k3sConfig.Args, " "))); err != nil {
return err
}

Expand Down
7 changes: 6 additions & 1 deletion internal/role/master.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,12 @@ func Master(cc *config.Config) Role {
args = append(args, k3sConfig.Args...)
}

if err := svc.OverrideCmd(fmt.Sprintf("/usr/bin/k3s server %s", strings.Join(args, " "))); err != nil {
k3sbin := utils.K3sBin()
if k3sbin == "" {
return fmt.Errorf("no k3s binary found (?)")
}

if err := svc.OverrideCmd(fmt.Sprintf("%s server %s", k3sbin, strings.Join(args, " "))); err != nil {
return err
}

Expand Down
Loading

0 comments on commit ea88699

Please sign in to comment.