Files
sealos/pkg/bootstrap/bootstrap.go
T
fengxsong 0eb9962229 feat: run sealos without root privileges (#2163)
* refactor: migrate buildah's subcommands

Signed-off-by: fengxsong <fengxsong@outlook.com>

* feat: rootless mode(almost done)

Signed-off-by: fengxsong <fengxsong@outlook.com>

* fix: set default values for --platform

Signed-off-by: fengxsong <fengxsong@outlook.com>

* fix: meet ci requirements

Signed-off-by: fengxsong <fengxsong@outlook.com>

* ci: add build-deps

Signed-off-by: fengxsong <fengxsong@outlook.com>

* style: define constants

Signed-off-by: fengxsong <fengxsong@outlook.com>

* feat: provide function to register default appliers

Signed-off-by: fengxsong <fengxsong@outlook.com>

* style: fix short descriptions of all subcommands

Signed-off-by: fengxsong <fengxsong@outlook.com>

Signed-off-by: fengxsong <fengxsong@outlook.com>
2022-12-02 14:59:14 +08:00

201 lines
5.0 KiB
Go

// Copyright © 2022 sealos.
//
// 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 bootstrap
import (
"context"
"fmt"
"golang.org/x/sync/errgroup"
v2 "github.com/labring/sealos/pkg/types/v1beta1"
"github.com/labring/sealos/pkg/utils/logger"
)
type Phase string
const (
Preflight Phase = "preflight"
Init Phase = "init"
Addon Phase = "addon"
)
type Interface interface {
Preflight(hosts ...string) error
Init(hosts ...string) error
RegisterApplier(Phase, ...Applier) error
ApplyAddons(hosts ...string) error
Reset(hosts ...string) error
}
type realBootstrap struct {
ctx Context
checks []Applier
initializers []Applier
addons []Applier
}
type shellWrapper func(string, string) string
func New(cluster *v2.Cluster) Interface {
ctx := NewContextFrom(cluster)
bs := &realBootstrap{
ctx: ctx,
checks: make([]Applier, 0),
initializers: make([]Applier, 0),
addons: make([]Applier, 0),
}
// register builtin appliers
_ = bs.RegisterApplier(Preflight, defaultCheckers...)
_ = bs.RegisterApplier(Init, defaultInitializers...)
_ = bs.RegisterApplier(Addon, defaultAddons...)
return bs
}
func (bs *realBootstrap) Preflight(hosts ...string) error {
return bs.apply(bs.checks, hosts...)
}
func (bs *realBootstrap) Init(hosts ...string) error {
return bs.apply(bs.initializers, hosts...)
}
func (bs *realBootstrap) ApplyAddons(hosts ...string) error {
return bs.apply(bs.addons, hosts...)
}
func (bs *realBootstrap) apply(appliers []Applier, hosts ...string) error {
return runParallel(hosts, func(host string) error {
for i := range appliers {
applier := appliers[i]
if !applier.Filter(bs.ctx, host) {
return nil
}
logger.Debug("apply %s on host %s", applier.Name(), host)
if err := applier.Apply(bs.ctx, host); err != nil {
return err
}
}
return nil
})
}
func (bs *realBootstrap) RegisterApplier(phase Phase, appliers ...Applier) error {
switch phase {
case Preflight:
bs.checks = append(bs.checks, appliers...)
case Init:
bs.initializers = append(bs.initializers, appliers...)
case Addon:
bs.addons = append(bs.addons, appliers...)
default:
return fmt.Errorf("unknown phase %s", phase)
}
return nil
}
func (bs *realBootstrap) Reset(hosts ...string) error {
appliers := make([]Applier, 0)
// only undo addons OR?
appliers = append(appliers, bs.addons...)
return runParallel(hosts, func(host string) error {
for i := range appliers {
applier := appliers[i]
if !applier.Filter(bs.ctx, host) {
return nil
}
logger.Debug("undo %s on host %s", applier.Name(), host)
if err := applier.Undo(bs.ctx, host); err != nil {
return err
}
}
return nil
})
}
func runParallel(hosts []string, fn func(string) error) error {
eg, _ := errgroup.WithContext(context.Background())
for i := range hosts {
host := hosts[i]
eg.Go(func() error {
return fn(host)
})
}
return eg.Wait()
}
type defaultChecker struct {
is *ImageShim
}
func (c *defaultChecker) Name() string {
return "default checker"
}
func (c *defaultChecker) Filter(_ Context, _ string) bool {
return true
}
func (c *defaultChecker) Apply(ctx Context, host string) error {
if c.is == nil {
c.is = NewImageShimHelper(ctx.GetExecer(), ctx.GetCluster().GetRegistryIP())
}
shimCmd := c.is.ApplyCMD(ctx.GetData().RootFSPath())
cmds := []string{ctx.GetShellWrapper()(host, ctx.GetBash().CheckBash()), shimCmd}
return ctx.GetExecer().CmdAsync(host, cmds...)
}
func (c *defaultChecker) Undo(_ Context, _ string) error {
return nil
}
type defaultInitializer struct{}
func (initializer *defaultInitializer) Name() string {
return "default initializer"
}
func (initializer *defaultInitializer) Filter(_ Context, _ string) bool {
return true
}
func (initializer *defaultInitializer) Apply(ctx Context, host string) error {
cmds := []string{ctx.GetShellWrapper()(host, ctx.GetBash().InitBash())}
return ctx.GetExecer().CmdAsync(host, cmds...)
}
func (initializer *defaultInitializer) Undo(_ Context, _ string) error {
return nil
}
func init() {
defaultCheckers = append(defaultCheckers, &defaultChecker{})
defaultInitializers = append(defaultInitializers, &defaultInitializer{})
}
func RegisterApplier(phase Phase, appliers ...Applier) error {
switch phase {
case Preflight:
defaultCheckers = append(defaultCheckers, appliers...)
case Init:
defaultInitializers = append(defaultInitializers, appliers...)
case Addon:
defaultAddons = append(defaultAddons, appliers...)
default:
return fmt.Errorf("unknown phase %s", phase)
}
return nil
}