This commit is contained in:
Josh Wolf
2021-05-27 23:25:40 -06:00
parent 7205de92d2
commit e1b0b2625c
23 changed files with 1569 additions and 23 deletions
+80
View File
@@ -0,0 +1,80 @@
package app
import (
"context"
"fmt"
"github.com/rancherfederal/hauler/pkg/apis/driver"
"github.com/rancherfederal/hauler/pkg/apis/haul"
"github.com/rancherfederal/hauler/pkg/create"
"github.com/rancherfederal/hauler/pkg/log"
"github.com/spf13/cobra"
"gopkg.in/yaml.v3"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type createOpts struct {
}
func NewCreateCommand() *cobra.Command {
opts := &createOpts{}
cmd := &cobra.Command{
Use: "create",
Short: "create a haul",
PreRunE: func(cmd *cobra.Command, args []string) error {
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
return opts.Run()
},
}
return cmd
}
func (o createOpts) Run() error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
logger := log.NewPrettyLogger()
// TODO: Load this from config if provided
h := haul.Haul{
TypeMeta: metav1.TypeMeta{
Kind: "Haul",
APIVersion: "v1alpha1",
},
Metadata: metav1.ObjectMeta{
Name: "haul",
},
Spec: haul.HaulSpec{
Driver: driver.K3sDriver{
Version: driver.K3sDefaultVersion,
},
PreloadImages: []string{
"plndr/kube-vip:0.3.4",
"registry:2.7.1",
"gitea/gitea:1.14.1-rootless",
},
},
}
d, err := yaml.Marshal(h)
if err != nil {
return err
}
fmt.Println(string(d))
c, err := create.NewCreator(logger)
if err != nil {
return err
}
_ = c
_ = ctx
//if err := c.Create(ctx, h); err != nil {
// return err
//}
return nil
}
+43
View File
@@ -3,7 +3,11 @@ package app
import (
"context"
"github.com/rancherfederal/hauler/pkg/deployer"
"github.com/rancherfederal/hauler/pkg/kube"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/cli-utils/pkg/object"
"time"
)
type deployOpts struct {
@@ -40,5 +44,44 @@ func (o *deployOpts) Run(haul string) error {
return err
}
err := waitForReady()
if err != nil {
return err
}
return nil
}
// waitForReady will wait for the cluster components to be ready
// TODO: Make components dynamic based on what is auto-loaded
func waitForReady() error {
cfg, err := kube.NewKubeClientConfig()
if err != nil {
return err
}
checker, err := kube.NewStatusChecker(cfg, 5*time.Second, 30*time.Minute)
if err != nil {
return err
}
objs, err := buildComponentRefs()
if err := checker.WaitForCondition(objs...); err != nil {
return err
}
return nil
}
func buildComponentRefs() ([]object.ObjMetadata, error) {
var objRefs []object.ObjMetadata
for _, deployment := range []string{"coredns", "local-path-provisioner"} {
objMeta, err := object.CreateObjMetadata("kube-system", deployment, schema.GroupKind{Group: "apps", Kind: "Deployment"})
if err != nil {
return nil, err
}
objRefs = append(objRefs, objMeta)
}
return objRefs, nil
}
+42
View File
@@ -0,0 +1,42 @@
package app
import (
"github.com/containerd/containerd/remotes"
"github.com/containerd/containerd/remotes/docker"
"github.com/spf13/cobra"
)
type ociOpts struct {
insecure bool
plainHTTP bool
}
const (
haulerMediaType = "application/vnd.oci.image"
)
func NewOCICommand() *cobra.Command {
opts := ociOpts{}
cmd := &cobra.Command{
Use: "oci",
Short: "oci stuff",
RunE: func(cmd *cobra.Command, args []string) error {
return cmd.Help()
},
}
cmd.AddCommand(NewOCIPushCommand())
cmd.AddCommand(NewOCIPullCommand())
f := cmd.Flags()
f.BoolVarP(&opts.insecure, "insecure", "", false, "Connect to registry without certs")
f.BoolVarP(&opts.plainHTTP, "plain-http", "", false, "Connect to registry over plain http")
return cmd
}
func (o *ociOpts) resolver() (remotes.Resolver, error) {
resolver := docker.NewResolver(docker.ResolverOptions{PlainHTTP: true})
return resolver, nil
}
+67
View File
@@ -0,0 +1,67 @@
package app
import (
"context"
"github.com/oras-project/oras-go/pkg/content"
"github.com/oras-project/oras-go/pkg/oras"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
type ociPullOpts struct {
ociOpts
sourceRef string
outDir string
}
func NewOCIPullCommand() *cobra.Command {
opts := ociPullOpts{}
cmd := &cobra.Command{
Use: "pull",
Short: "oci pull",
Aliases: []string{"p"},
Args: cobra.MinimumNArgs(1),
PreRunE: func(cmd *cobra.Command, args []string) error {
return opts.PreRun()
},
RunE: func(cmd *cobra.Command, args []string) error {
opts.sourceRef = args[0]
return opts.Run()
},
}
f := cmd.Flags()
f.StringVarP(&opts.outDir, "out-dir", "o", ".", "output directory")
return cmd
}
func (o *ociPullOpts) PreRun() error {
return nil
}
func (o *ociPullOpts) Run() error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
store := content.NewFileStore(o.outDir)
defer store.Close()
allowedMediaTypes := []string{
haulerMediaType,
}
resolver, err := o.resolver()
if err != nil {
return err
}
desc, _, err := oras.Pull(ctx, resolver, o.sourceRef, store, oras.WithAllowedMediaTypes(allowedMediaTypes))
logrus.Infof("pulled %s with digest: %s", o.sourceRef, desc.Digest)
return nil
}
+74
View File
@@ -0,0 +1,74 @@
package app
import (
"context"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/oras-project/oras-go/pkg/content"
"github.com/oras-project/oras-go/pkg/oras"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"os"
)
type ociPushOpts struct {
ociOpts
targetRef string
pathRef string
}
func NewOCIPushCommand() *cobra.Command {
opts := ociPushOpts{}
cmd := &cobra.Command{
Use: "push",
Short: "oci push",
Aliases: []string{"p"},
Args: cobra.MinimumNArgs(2),
PreRunE: func(cmd *cobra.Command, args []string) error {
return opts.PreRun()
},
RunE: func(cmd *cobra.Command, args []string) error {
opts.pathRef = args[0]
opts.targetRef = args[1]
return opts.Run()
},
}
return cmd
}
func (o *ociPushOpts) PreRun() error {
return nil
}
func (o *ociPushOpts) Run() error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
data, err := os.ReadFile(o.pathRef)
if err != nil {
return err
}
resolver, err := o.resolver()
if err != nil {
return err
}
store := content.NewMemoryStore()
contents := []ocispec.Descriptor{
store.Add(o.pathRef, haulerMediaType, data),
}
desc, err := oras.Push(ctx, resolver, o.targetRef, store, contents)
if err != nil {
return err
}
logrus.Infof("pushed %s to %s with digest: %s", o.pathRef, o.targetRef, desc.Digest)
return nil
}
+1 -1
View File
@@ -25,7 +25,7 @@ func NewPackageCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "package",
Short: "package all dependencies into a compressed archive",
Long: `package all dependencies into a compresed archive used by deploy.
Long: `package all dependencies into a compressed archive used by deploy.
Container images, git repositories, and more, packaged and ready to be served within an air gap.`,
Aliases: []string{"p", "pkg"},
+2
View File
@@ -39,6 +39,8 @@ then deploy the package into your air-gapped environment.`,
cmd.AddCommand(NewPackageCommand())
cmd.AddCommand(NewDeployCommand())
cmd.AddCommand(NewBundleCommand())
cmd.AddCommand(NewOCICommand())
cmd.AddCommand(NewCreateCommand())
f := cmd.PersistentFlags()
f.StringVarP(&loglevel, "loglevel", "l", "info",
+13 -6
View File
@@ -3,7 +3,7 @@ module github.com/rancherfederal/hauler
go 1.16
require (
github.com/docker/cli v20.10.6+incompatible // indirect
github.com/containerd/containerd v1.5.0-rc.3
github.com/docker/docker v20.10.6+incompatible // indirect
github.com/go-git/go-git/v5 v5.4.0
github.com/google/go-cmp v0.5.5 // indirect
@@ -11,16 +11,23 @@ require (
github.com/imdario/mergo v0.3.12
github.com/klauspost/compress v1.12.2 // indirect
github.com/mholt/archiver/v3 v3.5.0
github.com/pelletier/go-toml v1.8.1 // indirect
github.com/opencontainers/image-spec v1.0.1
github.com/oras-project/oras-go v0.1.0
github.com/rs/zerolog v1.22.0 // indirect
github.com/sirupsen/logrus v1.8.1
github.com/spf13/afero v1.2.2 // indirect
github.com/spf13/cobra v1.1.3
github.com/spf13/viper v1.7.0
github.com/ulikunitz/xz v0.5.8 // indirect
go.uber.org/multierr v1.7.0 // indirect
go.uber.org/zap v1.16.0
golang.org/x/net v0.0.0-20210521195947-fe42d452be8f
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20210521203332-0cec03c779c1 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
gotest.tools/v3 v3.0.3 // indirect
k8s.io/apimachinery v0.20.6
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
k8s.io/api v0.21.1
k8s.io/apimachinery v0.21.1
k8s.io/client-go v0.21.1
rsc.io/letsencrypt v0.0.3 // indirect
sigs.k8s.io/cli-utils v0.25.0
sigs.k8s.io/controller-runtime v0.8.3
)
+688 -7
View File
File diff suppressed because it is too large Load Diff
+41
View File
@@ -0,0 +1,41 @@
package driver
import (
"bufio"
"io"
)
const (
EtcPath = "/etc/rancher"
ExecutableBin = "hauler/bin"
)
type Driver interface {
Name() string
Images() []string
ReleaseArtifactsURL() string
AutodeployManifestsPath() string
PreloadImagesPath() string
AnonymousStaticPath() string
}
type DriverConfig struct {
NodeName string `json:"node-name" yaml:"node-name"`
KubeConfigMode string `json:"write-kubeconfig-mode" yaml:"write-kubeconfig-mode"`
NodeLabels []string `json:"node-label" yaml:"node-label"`
}
func linesToSlice(r io.ReadCloser) ([]string, error) {
var lines []string
scanner := bufio.NewScanner(r)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
if err := scanner.Err(); err != nil {
return nil, err
}
return lines, nil
}
+49
View File
@@ -0,0 +1,49 @@
package driver
import (
"fmt"
"net/http"
"path/filepath"
)
const (
K3sDefaultReleasesURL = "https://github.com/k3s-io/k3s/releases/download"
K3sDefaultVersion = "v1.21.1+k3s1"
K3sExecutable = "k3s"
)
const (
k3sDriverName = "k3s"
)
type K3sDriver struct {
Version string `json:"version" yaml:"version"`
Config K3sConfig `json:"config"`
}
type K3sConfig struct {
DriverConfig
}
func (k K3sDriver) Name() string { return k3sDriverName }
func (k K3sDriver) Images() []string {
//TODO: Don't panic!!
resp, err := http.Get(fmt.Sprintf("%s/%s/%s-images.txt", K3sDefaultReleasesURL, k.Version, k.Name()))
if err != nil {
panic("failed getting images")
}
defer resp.Body.Close()
images, err := linesToSlice(resp.Body)
if err != nil {
panic("failed getting images")
}
return images
}
func (k K3sDriver) ReleaseArtifactsURL() string { return fmt.Sprintf("%s/%s", K3sDefaultReleasesURL, k.Version) }
func (k K3sDriver) PreloadImagesPath() string { return filepath.Join(k3sDriverName, "agent/images") }
func (k K3sDriver) AutodeployManifestsPath() string { return filepath.Join(k3sDriverName, "server/manifests") }
func (k K3sDriver) AnonymousStaticPath() string { return filepath.Join(k3sDriverName, "server/static") }
+1
View File
@@ -0,0 +1 @@
package driver
+19
View File
@@ -0,0 +1,19 @@
package haul
import (
"github.com/rancherfederal/hauler/pkg/apis/driver"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type Haul struct {
metav1.TypeMeta `json:",inline" yaml:",inline"`
Metadata metav1.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
Spec HaulSpec `json:"spec" yaml:"spec"`
}
type HaulSpec struct {
Driver driver.Driver
PreloadImages []string `json:"preloadedImages" yaml:"preloadedImages"`
}
+49
View File
@@ -0,0 +1,49 @@
package archive
import (
"fmt"
"github.com/mholt/archiver/v3"
"os"
"path/filepath"
)
type Archiver interface {
// TODO: This isn't the greatest interface...
Archive([]string, string) error
String() string
}
type zstdArchiver archiver.TarZstd
func NewArchiver() *archiver.TarZstd {
z := &archiver.TarZstd{
Tar: &archiver.Tar{
OverwriteExisting: true,
MkdirAll: true,
ImplicitTopLevelFolder: false,
StripComponents: 0,
ContinueOnError: false,
},
}
return z
}
func CompressAndArchive(a Archiver, source string, dest string) error {
cwd, err := os.Getwd()
if err != nil {
return err
}
err = os.Chdir(source)
if err != nil {
return err
}
defer os.Chdir(cwd)
archivePath := filepath.Join(cwd, fmt.Sprintf("%s.%s", dest, a.String()))
if err := a.Archive([]string{"."}, archivePath); err != nil {
return err
}
return nil
}
+47
View File
@@ -0,0 +1,47 @@
package bootstrap
import (
"context"
"github.com/rancherfederal/hauler/pkg/apis/haul"
"github.com/rancherfederal/hauler/pkg/archive"
"os"
)
type bootstrapper struct {
haul haul.Haul
}
func NewBootstrapper(haulPath string) *bootstrapper {
tmpdir, err := os.MkdirTemp("", "hauler")
if err != nil {
return nil
}
defer os.Remove(tmpdir)
a := archive.NewArchiver()
err = a.Unarchive(haulPath, tmpdir)
var h haul.Haul
return &bootstrapper{
haul: h,
}
}
func (b bootstrapper) Bootstrap(ctx context.Context) error {
a := archive.NewArchiver()
_ = a
tmpdir, err := os.MkdirTemp("", "hauler")
if err != nil {
return err
}
defer os.Remove(tmpdir)
err = a.Unarchive(".", tmpdir)
if err != nil {
return err
}
return nil
}
+23 -7
View File
@@ -2,7 +2,6 @@ package bundle
import (
"context"
"fmt"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
@@ -77,13 +76,30 @@ func (l *LayoutStore) Add(ctx context.Context, imageName string) error {
return nil
}
func (l *LayoutStore) Push(ctx context.Context, imageName string) error {
ii, err := l.layout.ImageIndex()
if err != nil {
return err
}
func (l *LayoutStore) Find(ctx context.Context, imageName string) error {
//ii, err := l.layout.ImageIndex()
//if err != nil {
// return err
//}
//
//im, err := ii.IndexManifest()
//if err != nil {
// return err
//}
//
//for _, m := range im.Manifests {
// if ref, ok := m.Annotations[refNameAnnotation]; ok {
// r, err := l.layout.Image(m.Digest)
// if err != nil {
// return err
// }
// if r == imageName {
// return
// }
// }
//}
fmt.Println(ii)
return nil
}
func (l *LayoutStore) appendImage(img v1.Image, ref name.Reference, options ...layout.Option) error {
+120
View File
@@ -0,0 +1,120 @@
package create
import (
"context"
"fmt"
"github.com/rancherfederal/hauler/pkg/apis/driver"
"github.com/rancherfederal/hauler/pkg/apis/haul"
"github.com/rancherfederal/hauler/pkg/archive"
"github.com/rancherfederal/hauler/pkg/fetcher"
"github.com/rancherfederal/hauler/pkg/log"
"github.com/rs/zerolog"
"k8s.io/apimachinery/pkg/util/json"
"os"
"path/filepath"
)
type Creator struct {
logger zerolog.Logger
}
func NewCreator(logger zerolog.Logger) (*Creator, error) {
return &Creator{
logger: logger,
}, nil
}
func (c Creator) Create(ctx context.Context, h haul.Haul) error {
tmpdir, err := os.MkdirTemp("", "hauler")
if err != nil {
return err
}
defer os.Remove(tmpdir)
if err := c.buildHaulLayout(h.Spec.Driver, tmpdir); err != nil {
return err
}
if err := c.stamp(h.Spec.Driver, tmpdir); err != nil {
return err
}
err = saveDriverExecutable(ctx, h.Spec.Driver, tmpdir, &c.logger)
if err != nil {
return err
}
archivePath := filepath.Join(tmpdir, h.Spec.Driver.PreloadImagesPath(), fmt.Sprintf("%s.tar", h.Metadata.Name))
err = SavePreloadImages(ctx, h, archivePath, &c.logger)
if err != nil {
return err
}
a := archive.NewArchiver()
err = archive.CompressAndArchive(a, tmpdir, h.Metadata.Name)
if err != nil {
return err
}
return nil
}
func (c Creator) stamp(d driver.Driver, dir string) error {
data, err := json.Marshal(d)
if err != nil {
return err
}
haulerCfgFile := filepath.Join(dir, "hauler.json")
err = os.WriteFile(haulerCfgFile, data, 0644)
if err != nil {
return err
}
return nil
}
func (c Creator) buildHaulLayout(d driver.Driver, dir string) error {
c.logger.Info().Msgf("building package layout in %s", dir)
preloadImagesPath := filepath.Join(dir, d.PreloadImagesPath())
c.logger.Debug().Msgf("Creating directory for preloaded images: %s", preloadImagesPath)
if err := os.MkdirAll(preloadImagesPath, 0755); err != nil {
return err
}
autodeployManifestsPath := filepath.Join(dir, d.AutodeployManifestsPath())
c.logger.Debug().Msgf("Creating directory for autodeployed resources: %s", autodeployManifestsPath)
if err := os.MkdirAll(autodeployManifestsPath, 0700); err != nil {
return err
}
anonymousStaticPath := filepath.Join(dir, d.AnonymousStaticPath())
c.logger.Debug().Msgf("Creating directory for content to host anonymously: %s", anonymousStaticPath)
if err := os.MkdirAll(anonymousStaticPath, 0700); err != nil {
return err
}
driverExecutablePath := filepath.Join(dir, driver.ExecutableBin)
c.logger.Debug().Msgf("Creating directory for driver executable: %s", driverExecutablePath)
if err := os.MkdirAll(driverExecutablePath, 0755); err != nil {
return err
}
return nil
}
func saveDriverExecutable(ctx context.Context, d driver.Driver, dir string, logger log.Logger) error {
logger.Info().Msgf("Fetching %s executable", d.Name())
rawUrl := fmt.Sprintf("%s/%s", d.ReleaseArtifactsURL(), driver.K3sExecutable)
f := fetcher.FileFetcher{}
dst := filepath.Join(dir, driver.ExecutableBin, driver.K3sExecutable)
err := f.Get(ctx, rawUrl, dst)
if err != nil {
return err
}
return nil
}
+45
View File
@@ -0,0 +1,45 @@
package create
import (
"context"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/google/go-containerregistry/pkg/v1/tarball"
"github.com/rancherfederal/hauler/pkg/apis/haul"
"github.com/rancherfederal/hauler/pkg/log"
)
func SavePreloadImages(ctx context.Context, h haul.Haul, archivePath string, logger log.Logger) error {
imageRefs := make(map[name.Reference]v1.Image)
images := listImages(h)
for _, image := range images {
ref, err := name.ParseReference(image)
if err != nil {
return err
}
logger.Info().Msgf("identified %s", ref.Name())
img, err := remote.Image(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain))
if err != nil {
return err
}
imageRefs[ref] = img
}
logger.Info().Msgf("saving %d images to preload as %s", len(images), archivePath)
if err := tarball.MultiRefWriteToFile(archivePath, imageRefs); err != nil {
return err
}
return nil
}
func listImages(h haul.Haul) []string {
images := h.Spec.Driver.Images()
images = append(images, h.Spec.PreloadImages...)
return images
}
+2 -1
View File
@@ -103,7 +103,7 @@ func (d *Deployer) explode(pkg string) error {
// config will create a driver config if not exists, and merge if one exists
func (d *Deployer) config() error {
cfgFilePath := filepath.Join(v1alpha1.DriverEtcPath, d.Cluster.Driver.String(), "config.yaml")
err := os.MkdirAll(filepath.Dir(cfgFilePath), 0666)
err := os.MkdirAll(filepath.Dir(cfgFilePath), 0755)
if err != nil {
return err
}
@@ -125,6 +125,7 @@ func (d *Deployer) config() error {
func (d *Deployer) start() error {
cmd := exec.Command("/bin/sh", "/opt/hauler/bin/k3s-init.sh")
cmd.Env = append(os.Environ(), []string{
"INSTALL_K3S_EXEC=--write-kubeconfig-mode '0644'",
"INSTALL_K3S_SKIP_DOWNLOAD=true",
"INSTALL_K3S_SELINUX_WARN=true",
"INSTALL_K3S_SKIP_SELINUX_RPM=true",
+48
View File
@@ -0,0 +1,48 @@
package kube
import (
"github.com/rancherfederal/hauler/pkg/apis/hauler.cattle.io/v1alpha1"
corev1 "k8s.io/api/core/v1"
apiruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"path/filepath"
"sigs.k8s.io/controller-runtime/pkg/client"
)
func NewKubeClient() (client.Client, error) {
cfg, err := NewKubeClientConfig()
if err != nil {
return nil, err
}
scheme := apiruntime.NewScheme()
_ = corev1.AddToScheme(scheme)
kubeClient, err := client.New(cfg, client.Options{
Scheme: scheme,
})
if err != nil {
return nil, err
}
return kubeClient, nil
}
func NewKubeClientConfig() (*rest.Config, error) {
loadingRules := loadingRules()
configOverrides := &clientcmd.ConfigOverrides{}
kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides)
cfg, err := kubeConfig.ClientConfig()
return cfg, err
}
func loadingRules() *clientcmd.ClientConfigLoadingRules {
return &clientcmd.ClientConfigLoadingRules{
Precedence: []string{
filepath.Join(v1alpha1.DriverEtcPath, v1alpha1.DriverK3S, "k3s.yaml"),
filepath.Join(v1alpha1.DriverEtcPath, v1alpha1.DriverRKE2, "rke2.yaml"),
},
WarnIfAllMissing: true,
}
}
+94
View File
@@ -0,0 +1,94 @@
package kube
import (
"context"
"fmt"
"github.com/sirupsen/logrus"
"k8s.io/client-go/rest"
"sigs.k8s.io/cli-utils/pkg/kstatus/polling"
"sigs.k8s.io/cli-utils/pkg/kstatus/polling/aggregator"
"sigs.k8s.io/cli-utils/pkg/kstatus/polling/collector"
"sigs.k8s.io/cli-utils/pkg/kstatus/polling/event"
"sigs.k8s.io/cli-utils/pkg/kstatus/status"
"sigs.k8s.io/cli-utils/pkg/object"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
"strings"
"time"
)
type StatusChecker struct {
poller *polling.StatusPoller
client client.Client
logger *logrus.Entry
interval time.Duration
timeout time.Duration
}
func NewStatusChecker(kubeConfig *rest.Config, interval time.Duration, timeout time.Duration) (*StatusChecker, error) {
restMapper, err := apiutil.NewDynamicRESTMapper(kubeConfig)
if err != nil {
return nil, err
}
c, err := client.New(kubeConfig, client.Options{Mapper: restMapper})
if err != nil {
return nil, err
}
return &StatusChecker{
poller: polling.NewStatusPoller(c, restMapper),
client: c,
logger: logrus.WithFields(logrus.Fields{}),
interval: interval,
timeout: timeout,
}, nil
}
func (sc *StatusChecker) WaitForCondition(objs ...object.ObjMetadata) error {
ctx, cancel := context.WithTimeout(context.Background(), sc.timeout)
defer cancel()
eventsChan := sc.poller.Poll(ctx, objs, polling.Options{
PollInterval: sc.interval,
UseCache: true,
})
coll := collector.NewResourceStatusCollector(objs)
done := coll.ListenWithObserver(eventsChan, desiredStatusNotifierFunc(cancel, status.CurrentStatus))
<-done
for _, rs := range coll.ResourceStatuses {
switch rs.Status {
case status.CurrentStatus:
fmt.Printf("%s: %s ready\n", rs.Identifier.Name, strings.ToLower(rs.Identifier.GroupKind.Kind))
case status.NotFoundStatus:
fmt.Println(fmt.Errorf("%s: %s not found", rs.Identifier.Name, strings.ToLower(rs.Identifier.GroupKind.Kind)))
default:
fmt.Println(fmt.Errorf("%s: %s not ready", rs.Identifier.Name, strings.ToLower(rs.Identifier.GroupKind.Kind)))
}
}
if coll.Error != nil || ctx.Err() == context.DeadlineExceeded {
return fmt.Errorf("timed out waiting for condition")
}
return nil
}
// desiredStatusNotifierFunc returns an Observer function for the
// ResourceStatusCollector that will cancel the context (using the cancelFunc)
// when all resources have reached the desired status.
func desiredStatusNotifierFunc(cancelFunc context.CancelFunc, desired status.Status) collector.ObserverFunc {
return func(rsc *collector.ResourceStatusCollector, _ event.Event) {
var rss []*event.ResourceStatus
for _, rs := range rsc.ResourceStatuses {
rss = append(rss, rs)
}
aggStatus := aggregator.AggregateStatus(rss, desired)
if aggStatus == desired {
cancelFunc()
}
}
}
+21
View File
@@ -0,0 +1,21 @@
package log
import (
"github.com/rs/zerolog"
"os"
)
type Logger interface {
Info() *zerolog.Event
Debug() *zerolog.Event
Error() *zerolog.Event
}
func NewPrettyLogger() zerolog.Logger {
output := zerolog.ConsoleWriter{
Out: os.Stdout,
}
logger := zerolog.New(output).With().Timestamp().Logger()
return logger
}
-1
View File
@@ -140,7 +140,6 @@ func (p *Packager) pkgPreloadImages(ctx context.Context, dir string, images []st
if err := tarball.MultiRefWriteToFile(dir, imageRefs); err != nil {
return err
}
return nil
}