mirror of
https://github.com/hauler-dev/hauler.git
synced 2026-08-27 14:07:17 +00:00
wip
This commit is contained in:
Binary file not shown.
@@ -1 +0,0 @@
|
||||
{"architecture":"amd64","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"ExposedPorts":{"5000/tcp":{}},"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/etc/docker/registry/config.yml"],"Image":"sha256:16d08570c8f60bc500abc6aa8de40dc570f76849f4e09e92dfb73eccd616b9ac","Volumes":{"/var/lib/registry":{}},"WorkingDir":"","Entrypoint":["/entrypoint.sh"],"OnBuild":null,"Labels":null},"container":"2a7522517dbdc8460dafc3c829a1af17303b46f4efdb87319e7237fc22b5e809","container_config":{"Hostname":"2a7522517dbd","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"ExposedPorts":{"5000/tcp":{}},"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"/etc/docker/registry/config.yml\"]"],"Image":"sha256:16d08570c8f60bc500abc6aa8de40dc570f76849f4e09e92dfb73eccd616b9ac","Volumes":{"/var/lib/registry":{}},"WorkingDir":"","Entrypoint":["/entrypoint.sh"],"OnBuild":null,"Labels":{}},"created":"2021-04-15T02:47:06.008149989Z","docker_version":"19.03.12","history":[{"created":"2021-04-14T19:19:56.19548415Z","created_by":"/bin/sh -c #(nop) ADD file:282b9d56236cae29600bf8b698cb0a865ab17db7beea0be6870f9de63e7d4f80 in / "},{"created":"2021-04-14T19:19:56.41508347Z","created_by":"/bin/sh -c #(nop) CMD [\"/bin/sh\"]","empty_layer":true},{"created":"2021-04-15T02:47:02.917510551Z","created_by":"/bin/sh -c set -ex \u0026\u0026 apk add --no-cache ca-certificates"},{"created":"2021-04-15T02:47:03.775592191Z","created_by":"/bin/sh -c #(nop) COPY file:21256ff7df5369f7ad2e19c6d020a644303aded200bdbec4d46648f38d55df78 in /bin/registry "},{"created":"2021-04-15T02:47:04.209181789Z","created_by":"/bin/sh -c #(nop) COPY file:4544cc1555469403b322faecc1cf1ca584667c43a6a60b17300f97840c04196e in /etc/docker/registry/config.yml "},{"created":"2021-04-15T02:47:04.475173565Z","created_by":"/bin/sh -c #(nop) VOLUME [/var/lib/registry]","empty_layer":true},{"created":"2021-04-15T02:47:04.891850515Z","created_by":"/bin/sh -c #(nop) EXPOSE 5000","empty_layer":true},{"created":"2021-04-15T02:47:05.327358635Z","created_by":"/bin/sh -c #(nop) COPY file:507caa54f88c1f3862e5876e09a108b2083630ba24c57ad124e356a2de861d62 in /entrypoint.sh "},{"created":"2021-04-15T02:47:05.670828729Z","created_by":"/bin/sh -c #(nop) ENTRYPOINT [\"/entrypoint.sh\"]","empty_layer":true},{"created":"2021-04-15T02:47:06.008149989Z","created_by":"/bin/sh -c #(nop) CMD [\"/etc/docker/registry/config.yml\"]","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:9a5d14f9f5503e55088666beef7e85a8d9625d4fa7418e2fe269e9c54bcb853c","sha256:de9819405bcf1beb534c0049077f51160af2cec312a812e8c2f091fba1f9c306","sha256:b4592cba0628522e3e5aeac4dd6733405d92e6e106dfde5acb266eefc41c8569","sha256:3764c3e89288ef5786f440a66a5981593ddceb8b273ec9465f45c1758d64ced3","sha256:7b9a3910f3c3cddca51015d4dc228a39660e5f1e1bf93a8aa9053b65a271297e"]}}
|
||||
Binary file not shown.
@@ -1,36 +0,0 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.docker.container.image.v1+json",
|
||||
"size": 3112,
|
||||
"digest": "sha256:1fd8e1b0bb7ef6cc63e0094e2fb5a35259bdce7041ef5e7e4c99694ded1041d7"
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||
"size": 2816246,
|
||||
"digest": "sha256:ddad3d7c1e96adf9153f8921a7c9790f880a390163df453be1566e9ef0d546e0"
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||
"size": 299663,
|
||||
"digest": "sha256:6eda6749503f60eb69077e148cba1015ec5808b0c1c2e6f027f7d1b960f6a3a9"
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||
"size": 6823927,
|
||||
"digest": "sha256:363ab70c2143bc121f037ba432cede225b7053200abba2a7a4ca24c2915a3998"
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||
"size": 402,
|
||||
"digest": "sha256:5b94580856e6fd27a74d55ab33fc3b53fd6f599f7147f38845a2482cc003cc19"
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||
"size": 213,
|
||||
"digest": "sha256:12008541203a024dab3a30542c87421e5a27dbb601b9c6f029b3827af138ccb3"
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
|
||||
{"architecture":"amd64","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh"],"Image":"sha256:d3d4554f8b07cf59894bfb3551e10f89a559b24ee0992c4900c54175596b1389","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"60a3cdd128a8b373b313ed3e1083ff45e6badaad5dca5187282b005c38d04712","container_config":{"Hostname":"60a3cdd128a8","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"/bin/sh\"]"],"Image":"sha256:d3d4554f8b07cf59894bfb3551e10f89a559b24ee0992c4900c54175596b1389","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2021-04-14T19:19:39.643236135Z","docker_version":"19.03.12","history":[{"created":"2021-04-14T19:19:39.267885491Z","created_by":"/bin/sh -c #(nop) ADD file:8ec69d882e7f29f0652d537557160e638168550f738d0d49f90a7ef96bf31787 in / "},{"created":"2021-04-14T19:19:39.643236135Z","created_by":"/bin/sh -c #(nop) CMD [\"/bin/sh\"]","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:b2d5eeeaba3a22b9b8aa97261957974a6bd65274ebd43e1d81d0a7b8b752b116"]}}
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.docker.container.image.v1+json",
|
||||
"size": 1472,
|
||||
"digest": "sha256:6dbb9cc54074106d46d4ccb330f2a40a682d49dda5f4844962b7dce9fe44aaec"
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||
"size": 2811969,
|
||||
"digest": "sha256:540db60ca9383eac9e418f78490994d0af424aab7bf6d0e47ac8ed4e2e9bcbba"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"manifests": [
|
||||
{
|
||||
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
||||
"size": 528,
|
||||
"digest": "sha256:def822f9851ca422481ec6fee59a9966f12b351c62ccb9aca841526ffaa9f748",
|
||||
"annotations": {
|
||||
"org.opencontainers.image.ref.name": "index.docker.io/library/alpine:latest"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
||||
"size": 1363,
|
||||
"digest": "sha256:42043edfae481178f07aa077fa872fcc242e276d302f4ac2026d9d2eb65b955f",
|
||||
"annotations": {
|
||||
"org.opencontainers.image.ref.name": "index.docker.io/library/registry:2.7.1"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"imageLayoutVersion": "1.0.0"
|
||||
}
|
||||
@@ -1,13 +1,18 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"context"
|
||||
"github.com/mholt/archiver/v3"
|
||||
"github.com/rancherfederal/hauler/pkg/apis/haul"
|
||||
"github.com/rancherfederal/hauler/pkg/bootstrap"
|
||||
"github.com/spf13/cobra"
|
||||
"gopkg.in/yaml.v3"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type deployOpts struct {
|
||||
haul string
|
||||
haulerDir string
|
||||
}
|
||||
|
||||
// NewBootstrapCommand create a new sub command of haulerctl that bootstraps a cluster
|
||||
@@ -26,11 +31,39 @@ func NewBootstrapCommand() *cobra.Command {
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.StringVarP(&opts.haulerDir, "hauler-dir", "", "/opt/hauler", "Directory to install hauler components in")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Run performs the operation.
|
||||
func (o *deployOpts) Run(haul string) error {
|
||||
fmt.Println("Bootstrap")
|
||||
func (o *deployOpts) Run(haulPath string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
|
||||
z := archiver.NewTarZstd()
|
||||
z.OverwriteExisting = true
|
||||
z.MkdirAll = true
|
||||
|
||||
err := z.Unarchive(haulPath, o.haulerDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
haulerCfgPath := filepath.Join(o.haulerDir, "hauler.yaml")
|
||||
data, err := os.ReadFile(haulerCfgPath)
|
||||
|
||||
var h haul.Haul
|
||||
if err := yaml.Unmarshal(data, &h); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bstrp := bootstrap.NewBootstrapper(h, o.haulerDir)
|
||||
err = bstrp.Bootstrap(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
+24
-16
@@ -1,16 +1,17 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"github.com/rancherfederal/hauler/pkg/apis/bundle"
|
||||
|
||||
"github.com/imdario/mergo"
|
||||
"github.com/rancherfederal/hauler/pkg/apis/driver"
|
||||
"github.com/rancherfederal/hauler/pkg/apis/haul"
|
||||
"github.com/rancherfederal/hauler/pkg/apis/hauler.cattle.io/v1alpha1"
|
||||
"github.com/rancherfederal/hauler/pkg/packager"
|
||||
"github.com/rancherfederal/hauler/pkg/create"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"k8s.io/apimachinery/pkg/util/json"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type createOpts struct {
|
||||
@@ -26,7 +27,7 @@ func NewCreateCommand() *cobra.Command {
|
||||
opts := &createOpts{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "package",
|
||||
Use: "create",
|
||||
Short: "package all dependencies into a compressed archive",
|
||||
Long: `package all dependencies into a compressed archive used by deploy.
|
||||
|
||||
@@ -74,20 +75,27 @@ func (o *createOpts) Run() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
|
||||
cluster := v1alpha1.NewDefaultCluster(o.driver)
|
||||
|
||||
// Merge user defined config with default config
|
||||
// TODO: This should be done with types... but we'll need mergo for more stuff so lazy approach here
|
||||
if err := mergo.Merge(cluster, o.userClusterConfig, mergo.WithOverride); err != nil {
|
||||
return err
|
||||
d := driver.K3sDriver{
|
||||
Version: "v1.21.1+k3s1",
|
||||
Config: driver.K3sConfig{},
|
||||
}
|
||||
|
||||
d, _ := json.Marshal(cluster)
|
||||
buf := bytes.NewReader(d)
|
||||
viper.ReadConfig(buf)
|
||||
h := haul.Haul{
|
||||
TypeMeta: metav1.TypeMeta{},
|
||||
Metadata: metav1.ObjectMeta{
|
||||
Name: "haul",
|
||||
},
|
||||
Spec: haul.HaulSpec{
|
||||
Driver: d,
|
||||
Bundles: []bundle.Bundle{
|
||||
bundle.CreateDriverBundle(d),
|
||||
{ Name: "test", Path: "testdata/bundle-a" },
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
pkg := packager.NewPackager(cluster)
|
||||
if err := pkg.Package(ctx, o.outputFile); err != nil {
|
||||
creator, _ := create.NewCreator()
|
||||
if err := creator.Create(ctx, h); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -8,17 +8,20 @@ require (
|
||||
github.com/go-git/go-git/v5 v5.4.0
|
||||
github.com/google/go-cmp v0.5.5 // indirect
|
||||
github.com/google/go-containerregistry v0.4.1
|
||||
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/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/pelletier/go-toml v1.8.1 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/opencontainers/image-spec v1.0.1
|
||||
github.com/oras-project/oras-go v0.1.0
|
||||
github.com/pterm/pterm v0.12.18
|
||||
github.com/rogpeppe/go-internal v1.3.0
|
||||
github.com/rs/zerolog v1.22.0
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
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
|
||||
go.uber.org/zap v1.16.0 // indirect
|
||||
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
|
||||
|
||||
@@ -44,7 +44,6 @@ github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6L
|
||||
github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
|
||||
@@ -101,6 +100,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/atomicgo/cursor v0.0.1 h1:xdogsqa6YYlLfM+GyClC/Lchf7aiMerFiZQn7soTOoU=
|
||||
github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk=
|
||||
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
|
||||
github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
@@ -491,6 +492,8 @@ github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1a
|
||||
github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
|
||||
github.com/googleapis/gnostic v0.5.1 h1:A8Yhf6EtqTv9RMsU6MQTyrtV1TjWlR6xU9BsZIwuTCM=
|
||||
github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
|
||||
github.com/gookit/color v1.4.2 h1:tXy44JFSFkKnELV6WaMo/lLfu/meqITX3iAV52do7lk=
|
||||
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
|
||||
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
@@ -609,6 +612,8 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.12 h1:Y41i/hVW3Pgwr8gV+J23B9YEY0zxjptBuCWEaxmAOow=
|
||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
|
||||
@@ -760,7 +765,13 @@ github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
|
||||
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/pterm/pterm v0.12.18 h1:lGPcpzyWQhPZS7N0NQWHLA5Q4GiZ5OBiQ3m/NiNOcJM=
|
||||
github.com/pterm/pterm v0.12.18/go.mod h1:KJSTVufsT+4ByOegmntwkTvDGpDhNQQG7034Klkn9kc=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.22.0 h1:XrVUjV4K+izZpKXZHlPrYQiDtmdGiCylnT4i43AAWxg=
|
||||
@@ -869,6 +880,8 @@ github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMx
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI=
|
||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
@@ -953,7 +966,6 @@ golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHl
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
@@ -962,7 +974,6 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -1101,6 +1112,7 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210521203332-0cec03c779c1 h1:lCnv+lfrU9FRPGf8NeRuWAAPjNnema5WtBinMgs1fD8=
|
||||
@@ -1175,7 +1187,6 @@ golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roY
|
||||
golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a h1:CB3a9Nez8M13wwlr/E2YtwoU+qYHKfC+JrDa45RXXoQ=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -1307,7 +1318,6 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78=
|
||||
k8s.io/api v0.18.10/go.mod h1:xWtwPX1v47j5RTncmlMFGCx8b0avh+nP8OgZZ9hjo3M=
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
package bundle
|
||||
|
||||
import (
|
||||
"github.com/rancherfederal/hauler/pkg/apis/driver"
|
||||
"github.com/rancherfederal/hauler/pkg/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
AutodeployDirectory = "autodeploy"
|
||||
StaticDirectory = "static"
|
||||
ImagePreloadDirectory = "images"
|
||||
)
|
||||
|
||||
type Bundle struct {
|
||||
Name string `yaml:"name"`
|
||||
Path string `yaml:"path"`
|
||||
|
||||
Images []string
|
||||
}
|
||||
|
||||
func CreateDriverBundle(d driver.Driver) Bundle {
|
||||
return Bundle{
|
||||
Name: d.Name(),
|
||||
Path: "",
|
||||
Images: d.Images(),
|
||||
}
|
||||
}
|
||||
|
||||
func (b Bundle) CreateLayout(root string) *util.FSLayout {
|
||||
l := util.NewLayout(root)
|
||||
l.AddDir(AutodeployDirectory, os.ModePerm)
|
||||
l.AddDir(StaticDirectory, os.ModePerm)
|
||||
l.AddDir(ImagePreloadDirectory, os.ModePerm)
|
||||
return l
|
||||
}
|
||||
|
||||
//ResolveBundle will resolve all provided references
|
||||
func (b *Bundle) ResolveBundleFromPath() error {
|
||||
if b.Path == "" {
|
||||
//Only need to resolve bundles that are sourced from a directory
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, err := os.Stat(b.Path); os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
manifestsPath := filepath.Join(b.Path, AutodeployDirectory)
|
||||
err := filepath.WalkDir(manifestsPath, func(path string, d fs.DirEntry, err error) error {
|
||||
if d.IsDir() { return nil }
|
||||
|
||||
ext := filepath.Ext(path)
|
||||
if !(strings.HasSuffix(ext, "yaml") || strings.HasSuffix(ext, "yml")) { return nil }
|
||||
|
||||
//TODO: Identify Images from manifests
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
imagesPath := filepath.Join(b.Path, ImagePreloadDirectory)
|
||||
err = filepath.WalkDir(imagesPath, func(path string, d fs.DirEntry, err error) error {
|
||||
if d.IsDir() { return nil }
|
||||
|
||||
ext := filepath.Ext(path)
|
||||
if strings.HasSuffix(ext, "txt") {
|
||||
//Try reading as text line
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
imgs, err := util.LinesToSlice(f)
|
||||
|
||||
b.Images = append(b.Images, imgs...)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b Bundle) Setup(d driver.Driver, path string) error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Chdir(cwd)
|
||||
|
||||
err = os.Chdir(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
targetStatic := filepath.Join(d.VarPath(), d.AnonymousStaticPath(), b.Name)
|
||||
if err := safeSymlink(StaticDirectory, targetStatic); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
targetImages := filepath.Join(d.VarPath(), d.PreloadImagesPath(), b.Name)
|
||||
if err := safeSymlink(ImagePreloadDirectory, targetImages); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
targetManifests := filepath.Join(d.VarPath(), d.AutodeployManifestsPath(), b.Name)
|
||||
if err := safeSymlink(AutodeployDirectory, targetManifests); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func safeSymlink(src, dst string) error {
|
||||
logrus.Infof("symlinking %s to %s", src, dst)
|
||||
if _, err := os.Stat(dst); err == nil {
|
||||
if err := os.RemoveAll(dst); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return os.Symlink(src, dst)
|
||||
}
|
||||
@@ -1,13 +1,9 @@
|
||||
package driver
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
EtcPath = "/etc/rancher"
|
||||
ExecutableBin = "hauler/bin"
|
||||
VarBasePath = "/var/lib/rancher"
|
||||
ExecutableBin = "bin"
|
||||
)
|
||||
|
||||
type Driver interface {
|
||||
@@ -18,24 +14,12 @@ type Driver interface {
|
||||
AutodeployManifestsPath() string
|
||||
PreloadImagesPath() string
|
||||
AnonymousStaticPath() string
|
||||
|
||||
VarPath() 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
|
||||
NodeName string `yaml:"node-name"`
|
||||
KubeConfigMode string `yaml:"write-kubeconfig-mode"`
|
||||
NodeLabels []string `yaml:"node-label"`
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package driver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/rancherfederal/hauler/pkg/util"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
)
|
||||
@@ -17,13 +18,13 @@ const (
|
||||
)
|
||||
|
||||
type K3sDriver struct {
|
||||
Version string `json:"version" yaml:"version"`
|
||||
Version string `yaml:"version"`
|
||||
|
||||
Config K3sConfig `json:"config"`
|
||||
Config K3sConfig
|
||||
}
|
||||
|
||||
type K3sConfig struct {
|
||||
DriverConfig
|
||||
DriverConfig `yaml:"config,inline"`
|
||||
}
|
||||
|
||||
func (k K3sDriver) Name() string { return k3sDriverName }
|
||||
@@ -35,7 +36,7 @@ func (k K3sDriver) Images() []string {
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
images, err := linesToSlice(resp.Body)
|
||||
images, err := util.LinesToSlice(resp.Body)
|
||||
if err != nil {
|
||||
panic("failed getting images")
|
||||
}
|
||||
@@ -44,6 +45,7 @@ func (k K3sDriver) Images() []string {
|
||||
}
|
||||
|
||||
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") }
|
||||
func (k K3sDriver) PreloadImagesPath() string { return "agent/images" }
|
||||
func (k K3sDriver) AutodeployManifestsPath() string { return "server/manifests" }
|
||||
func (k K3sDriver) AnonymousStaticPath() string { return "server/static" }
|
||||
func (k K3sDriver) VarPath() string { return filepath.Join(VarBasePath, k.Name()) }
|
||||
+21
-5
@@ -1,19 +1,35 @@
|
||||
package haul
|
||||
|
||||
import (
|
||||
"github.com/rancherfederal/hauler/pkg/apis/bundle"
|
||||
"github.com/rancherfederal/hauler/pkg/apis/driver"
|
||||
"github.com/rancherfederal/hauler/pkg/util"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type Haul struct {
|
||||
metav1.TypeMeta `json:",inline" yaml:",inline"`
|
||||
Metadata metav1.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
metav1.TypeMeta `yaml:",inline"`
|
||||
Metadata metav1.ObjectMeta `yaml:"metadata,omitempty"`
|
||||
|
||||
Spec HaulSpec `json:"spec" yaml:"spec"`
|
||||
Spec HaulSpec `yaml:"spec"`
|
||||
}
|
||||
|
||||
type HaulSpec struct {
|
||||
Driver driver.Driver
|
||||
Driver driver.K3sDriver `yaml:"driver"`
|
||||
|
||||
PreloadImages []string `json:"preloadedImages" yaml:"preloadedImages"`
|
||||
Bundles []bundle.Bundle `yaml:"bundles"`
|
||||
}
|
||||
|
||||
func (h Haul) CreateLayout(root string) *util.FSLayout {
|
||||
l := util.NewLayout(root)
|
||||
l.AddDir("bin", os.ModePerm)
|
||||
|
||||
for _, b := range h.Spec.Bundles {
|
||||
bundlePath := filepath.Join("bundles", b.Name)
|
||||
l.AddDir(bundlePath, os.ModePerm)
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
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
|
||||
}
|
||||
+82
-20
@@ -2,46 +2,108 @@ package bootstrap
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"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/util"
|
||||
"io/fs"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//go:embed bin/*
|
||||
var e embed.FS
|
||||
|
||||
type bootstrapper struct {
|
||||
haul haul.Haul
|
||||
dir string
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
func NewBootstrapper(h haul.Haul, haulerDir string) *bootstrapper {
|
||||
return &bootstrapper{
|
||||
haul: h,
|
||||
dir: haulerDir,
|
||||
}
|
||||
}
|
||||
|
||||
func (b bootstrapper) Bootstrap(ctx context.Context) error {
|
||||
a := archive.NewArchiver()
|
||||
_ = a
|
||||
|
||||
tmpdir, err := os.MkdirTemp("", "hauler")
|
||||
if err != nil {
|
||||
if err := b.renderEmbeddedDriverInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Remove(tmpdir)
|
||||
|
||||
err = a.Unarchive(".", tmpdir)
|
||||
if err := b.createLayout(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, bndl := range b.haul.Spec.Bundles {
|
||||
bundleBasePath := filepath.Join(b.dir, "bundles", bndl.Name)
|
||||
err := bndl.Setup(b.haul.Spec.Driver, bundleBasePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err := StartDriver(b.haul.Spec.Driver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b bootstrapper) renderEmbeddedDriverInit() error {
|
||||
err := fs.WalkDir(e, "bin", func(path string, d fs.DirEntry, err error) error {
|
||||
if d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if strings.Contains(path, b.haul.Spec.Driver.Name()) {
|
||||
data, err := fs.ReadFile(e, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
renderedFile := filepath.Join(b.dir, path)
|
||||
err = os.WriteFile(renderedFile, data, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b bootstrapper) createLayout() error {
|
||||
l := util.NewLayout(b.haul.Spec.Driver.VarPath())
|
||||
l.AddDir("server/static", 0700)
|
||||
l.AddDir("server/manifests", 0755)
|
||||
l.AddDir("agent/images", 0755)
|
||||
return l.Create()
|
||||
}
|
||||
|
||||
func StartDriver(d driver.Driver) error {
|
||||
cmd := exec.Command("/bin/sh", "/opt/hauler/bin/k3s-init.sh")
|
||||
cmd.Env = append(os.Environ(), []string{
|
||||
"INSTALL_K3S_SKIP_DOWNLOAD=true",
|
||||
"INSTALL_K3S_SELINUX_WARN=true",
|
||||
"INSTALL_K3S_SKIP_SELINUX_RPM=true",
|
||||
"INSTALL_K3S_BIN_DIR=/opt/hauler/bin",
|
||||
"INSTALL_K3S_SKIP_START=true",
|
||||
}...)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("with driver: %s\n%v", out, err)
|
||||
}
|
||||
|
||||
fmt.Println(string(out))
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
package bundle
|
||||
|
||||
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/empty"
|
||||
"github.com/google/go-containerregistry/pkg/v1/layout"
|
||||
"github.com/google/go-containerregistry/pkg/v1/remote"
|
||||
"github.com/sirupsen/logrus"
|
||||
"os"
|
||||
)
|
||||
|
||||
const refNameAnnotation = "org.opencontainers.image.ref.name"
|
||||
|
||||
type LayoutStore struct {
|
||||
Dir string
|
||||
layout layout.Path
|
||||
logger *logrus.Entry
|
||||
client *Client
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
|
||||
}
|
||||
|
||||
func NewLayoutStore(path string) *LayoutStore {
|
||||
lp, err := createLayoutIfNotExists(path)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &LayoutStore{
|
||||
Dir: path,
|
||||
layout: lp,
|
||||
logger: logrus.WithFields(logrus.Fields{
|
||||
"store": "hauler",
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
func createLayoutIfNotExists(path string) (layout.Path, error) {
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return "", err
|
||||
}
|
||||
if err := os.MkdirAll(path, 0755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
lp, err := layout.Write(path, empty.Index)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return lp, nil
|
||||
}
|
||||
|
||||
func (l *LayoutStore) Add(ctx context.Context, imageName string) error {
|
||||
ref, err := name.ParseReference(imageName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
img, err := remote.Image(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := l.appendImage(img, ref); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *LayoutStore) appendImage(img v1.Image, ref name.Reference, options ...layout.Option) error {
|
||||
if err := l.layout.WriteImage(img); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mt, err := img.MediaType()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d, err := img.Digest()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
manifest, err := img.RawManifest()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
annotations := map[string]string{
|
||||
refNameAnnotation: ref.Name(),
|
||||
}
|
||||
|
||||
desc := v1.Descriptor{
|
||||
MediaType: mt,
|
||||
Size: int64(len(manifest)),
|
||||
Digest: d,
|
||||
URLs: nil,
|
||||
Annotations: annotations,
|
||||
Platform: nil,
|
||||
}
|
||||
|
||||
for _, opt := range options {
|
||||
if err := opt(&desc); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return l.layout.AppendDescriptor(desc)
|
||||
}
|
||||
+5
-5
@@ -28,7 +28,7 @@ func NewCopier(path string, media string) *Copier {
|
||||
|
||||
return &Copier{
|
||||
Dir: path,
|
||||
fileStore: fs,
|
||||
fileStore: *fs,
|
||||
mediaType: media,
|
||||
logger: logrus.WithFields(logrus.Fields{
|
||||
"store": "hauler",
|
||||
@@ -36,13 +36,13 @@ func NewCopier(path string, media string) *Copier {
|
||||
}
|
||||
}
|
||||
|
||||
func createFileStoreIfNotExists(path string) (content.FileStore, error) {
|
||||
func createFileStoreIfNotExists(path string) (*content.FileStore, error) {
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
if err := os.MkdirAll(path, 0755); err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ func (c Copier) Get(ctx context.Context, src string) error {
|
||||
// Pull file(s) from registry and save to disk
|
||||
fmt.Printf("Pulling from %s and saving to %s...\n", src, c.Dir)
|
||||
allowedMediaTypes := []string{(c.mediaType)}
|
||||
desc, _, err := oras.Pull(ctx, resolver, src, c.fileStore, oras.WithAllowedMediaTypes(allowedMediaTypes))
|
||||
desc, _, err := oras.Pull(ctx, resolver, src, &c.fileStore, oras.WithAllowedMediaTypes(allowedMediaTypes))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package create
|
||||
|
||||
import "github.com/rancherfederal/hauler/pkg/apis/bundle"
|
||||
|
||||
func ResolveBundle(b bundle.Bundle) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
+53
-74
@@ -3,25 +3,22 @@ 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"
|
||||
"github.com/mholt/archiver/v3"
|
||||
"github.com/rancherfederal/hauler/pkg/apis/bundle"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gopkg.in/yaml.v3"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/rancherfederal/hauler/pkg/apis/driver"
|
||||
"github.com/rancherfederal/hauler/pkg/apis/haul"
|
||||
"github.com/rancherfederal/hauler/pkg/fetcher"
|
||||
)
|
||||
|
||||
type Creator struct {
|
||||
logger zerolog.Logger
|
||||
}
|
||||
type Creator struct{}
|
||||
|
||||
func NewCreator(logger zerolog.Logger) (*Creator, error) {
|
||||
return &Creator{
|
||||
logger: logger,
|
||||
}, nil
|
||||
func NewCreator() (*Creator, error) {
|
||||
return &Creator{}, nil
|
||||
}
|
||||
|
||||
func (c Creator) Create(ctx context.Context, h haul.Haul) error {
|
||||
@@ -31,27 +28,50 @@ func (c Creator) Create(ctx context.Context, h haul.Haul) error {
|
||||
}
|
||||
defer os.Remove(tmpdir)
|
||||
|
||||
if err := c.buildHaulLayout(h.Spec.Driver, tmpdir); err != nil {
|
||||
layout := h.CreateLayout(tmpdir)
|
||||
if err := layout.Create(); 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)
|
||||
err = saveDriverExecutable(ctx, h.Spec.Driver, tmpdir)
|
||||
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
|
||||
for _, b := range h.Spec.Bundles {
|
||||
logrus.Infof("Packaging bundle %s", b.Name)
|
||||
|
||||
err = b.ResolveBundleFromPath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bundlePath := filepath.Join(tmpdir, "bundles", b.Name)
|
||||
bl := b.CreateLayout(bundlePath)
|
||||
if err := bl.Create(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
imagesPath := filepath.Join(bundlePath, bundle.ImagePreloadDirectory, fmt.Sprintf("%s.tar", b.Name))
|
||||
err = SavePreloadImages(ctx, b.Images, imagesPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
a := archive.NewArchiver()
|
||||
err = archive.CompressAndArchive(a, tmpdir, h.Metadata.Name)
|
||||
if data, err := yaml.Marshal(h); err != nil {
|
||||
return err
|
||||
} else {
|
||||
haulerConfigPath := filepath.Join(tmpdir, "hauler.yaml")
|
||||
err = os.WriteFile(haulerConfigPath, data, os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
zstd := archiver.NewTarZstd()
|
||||
zstd.OverwriteExisting = true
|
||||
err = layout.Archive(zstd, h.Metadata.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -59,54 +79,8 @@ func (c Creator) Create(ctx context.Context, h haul.Haul) error {
|
||||
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)
|
||||
func saveDriverExecutable(ctx context.Context, d driver.Driver, dir string) error {
|
||||
rawUrl := fmt.Sprintf("%s/%s", d.ReleaseArtifactsURL(), d.Name())
|
||||
|
||||
f := fetcher.FileFetcher{}
|
||||
dst := filepath.Join(dir, driver.ExecutableBin, driver.K3sExecutable)
|
||||
@@ -116,5 +90,10 @@ func saveDriverExecutable(ctx context.Context, d driver.Driver, dir string, logg
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.Chmod(dst, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
+5
-13
@@ -7,13 +7,10 @@ import (
|
||||
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 {
|
||||
func SavePreloadImages(ctx context.Context, images []string, archivePath string) error {
|
||||
imageRefs := make(map[name.Reference]v1.Image)
|
||||
images := listImages(h)
|
||||
|
||||
for _, image := range images {
|
||||
ref, err := name.ParseReference(image)
|
||||
@@ -21,7 +18,6 @@ func SavePreloadImages(ctx context.Context, h haul.Haul, archivePath string, log
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info().Msgf("identified %s", ref.Name())
|
||||
img, err := remote.Image(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -30,16 +26,12 @@ func SavePreloadImages(ctx context.Context, h haul.Haul, archivePath string, log
|
||||
imageRefs[ref] = img
|
||||
}
|
||||
|
||||
logger.Info().Msgf("saving %d images to preload as %s", len(images), archivePath)
|
||||
if len(images) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
package deployer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/mholt/archiver/v3"
|
||||
"github.com/rancherfederal/hauler/pkg/apis/hauler.cattle.io/v1alpha1"
|
||||
"github.com/rancherfederal/hauler/pkg/embed"
|
||||
"github.com/sirupsen/logrus"
|
||||
"io/fs"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type Deployer struct {
|
||||
Cluster *v1alpha1.Cluster
|
||||
logger *logrus.Entry
|
||||
za *archiver.TarZstd
|
||||
}
|
||||
|
||||
func NewDeployer() *Deployer {
|
||||
return &Deployer{
|
||||
// TODO: Load this from config file
|
||||
Cluster: &v1alpha1.Cluster{
|
||||
TypeMeta: metav1.TypeMeta{},
|
||||
Metadata: metav1.ObjectMeta{},
|
||||
Driver: v1alpha1.K3SDriver{
|
||||
Version: "",
|
||||
ReleaseURL: "",
|
||||
},
|
||||
Arch: "amd64",
|
||||
PreloadImages: nil,
|
||||
AutodeployManifests: nil,
|
||||
},
|
||||
logger: logrus.WithFields(logrus.Fields{
|
||||
"cluster": "",
|
||||
"driver": "",
|
||||
"stage:": "deployer",
|
||||
}),
|
||||
za: &archiver.TarZstd{
|
||||
Tar: &archiver.Tar{
|
||||
OverwriteExisting: true,
|
||||
MkdirAll: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Deployer) Deploy(ctx context.Context, pkg string) error {
|
||||
d.logger.Infof("deploying cluster from %s", pkg)
|
||||
if err := d.explode(pkg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.logger.Infof("configuring %s", d.Cluster.Driver.String())
|
||||
if err := d.config(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.logger.Infof("starting %s", d.Cluster.Driver.String())
|
||||
if err := d.start(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Deployer) explode(pkg string) error {
|
||||
d.logger.Infof("extracting %s to %s", d.Cluster.Driver.String(), v1alpha1.DriverVarPath)
|
||||
if err := d.za.Extract(pkg, d.Cluster.Driver.String(), v1alpha1.DriverVarPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: This walks through the compressed archive twice, loss in speed is minimal but it's still not optimal
|
||||
d.logger.Infof("extracting %s to %s", v1alpha1.HaulerBin, "/opt/hauler")
|
||||
if err := d.za.Extract(pkg, v1alpha1.HaulerBin, "/opt/hauler"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.logger.Infof("rendering embedded files")
|
||||
if err := embed.RenderEmbedded(d.Cluster.Driver); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.logger.Debugf("ensuring everything in %s is executable", "/opt/hauler/bin")
|
||||
err := filepath.WalkDir("/opt/hauler/bin", func(path string, d fs.DirEntry, err error) error {
|
||||
if d.IsDir() {
|
||||
return err
|
||||
}
|
||||
err = os.Chmod(path, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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), 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := os.ReadFile(cfgFilePath); os.IsNotExist(err) {
|
||||
data, _ := d.Cluster.Driver.MarshalConfig()
|
||||
err := os.WriteFile(cfgFilePath, data, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
} else {
|
||||
// TODO: Merge configs
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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",
|
||||
"INSTALL_K3S_BIN_DIR=/opt/hauler/bin",
|
||||
}...)
|
||||
_, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Do this? or leave it up to user and print message
|
||||
//path := os.Getenv("PATH")
|
||||
//d.logger.Infof("adding /opt/hauler/bin to $PATH")
|
||||
//err = os.Setenv("PATH", fmt.Sprintf("%s:/opt/hauler/bin", path))
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
package embed
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"github.com/rancherfederal/hauler/pkg/apis/hauler.cattle.io/v1alpha1"
|
||||
"github.com/sirupsen/logrus"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const embedRootDir = "embedded"
|
||||
|
||||
//go:embed embedded/*
|
||||
var e embed.FS
|
||||
|
||||
type embedded struct {
|
||||
fs fs.FS
|
||||
embedMap []embedMap
|
||||
}
|
||||
|
||||
type embedMap struct {
|
||||
src string
|
||||
dst string
|
||||
mode os.FileMode
|
||||
}
|
||||
|
||||
func RenderEmbedded(d v1alpha1.Driver) error {
|
||||
efsys, err := fs.Sub(e, embedRootDir)
|
||||
if err != nil {
|
||||
logrus.Fatalf("failed to load embedded filesystem: %v", err)
|
||||
}
|
||||
|
||||
|
||||
e := &embedded{
|
||||
fs: efsys,
|
||||
embedMap: []embedMap{
|
||||
{
|
||||
src: "manifests",
|
||||
dst: filepath.Join(v1alpha1.DriverVarPath, d.String(), "server"),
|
||||
mode: 0600,
|
||||
},
|
||||
{
|
||||
src: "static",
|
||||
dst: filepath.Join(v1alpha1.DriverVarPath, d.String(), "server"),
|
||||
mode: 0600,
|
||||
},
|
||||
{
|
||||
src: "bin/k3s-init.sh",
|
||||
dst: filepath.Join("/opt", v1alpha1.HaulerBin, "k3s-init.sh"),
|
||||
mode: 0755,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := e.render(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *embedded) render() error {
|
||||
for _, em := range e.embedMap {
|
||||
f, err := fs.Stat(e.fs, em.src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if f.IsDir() {
|
||||
err := e.WalkDir(em.src, em.dst, em.mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err := e.WriteTo(em.src, em.dst, em.mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *embedded) WriteTo(src string, dest string, mode os.FileMode) error {
|
||||
logrus.Infof("creating file from embedded store: %s", src)
|
||||
|
||||
data, err := fs.ReadFile(e.fs, src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read file %s from embedded store: %v", src, err)
|
||||
}
|
||||
|
||||
err = os.WriteFile(dest, data, mode)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write %s to %s from embedded store: %v", src, dest, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *embedded) WalkDir(src string, dest string, mode os.FileMode) error {
|
||||
err := fs.WalkDir(e.fs, src, func(path string, d fs.DirEntry, err error) error {
|
||||
p := filepath.Join(dest, path)
|
||||
if d.IsDir() {
|
||||
logrus.Infof("creating directory: %s", p)
|
||||
err := os.Mkdir(filepath.Join(dest, path), mode)
|
||||
|
||||
//TODO: lol
|
||||
if err == fs.ErrExist {
|
||||
return fmt.Errorf("failed to create directory: %v", err)
|
||||
}
|
||||
} else {
|
||||
err := e.WriteTo(path, p, mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+4
-2
@@ -3,7 +3,7 @@ package fetcher
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/pterm/pterm"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -22,7 +22,7 @@ func (f FileFetcher) Get(ctx context.Context, src string, dst string) error {
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
logrus.Infof("Getting %s from %s", dst, src)
|
||||
spinner, _ := pterm.DefaultSpinner.Start("Fetching ", src, " and saving to ", dst)
|
||||
resp, err := http.Get(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting file: %w", err)
|
||||
@@ -30,6 +30,8 @@ func (f FileFetcher) Get(ctx context.Context, src string, dst string) error {
|
||||
defer resp.Body.Close()
|
||||
|
||||
_, err = io.Copy(out, resp.Body)
|
||||
|
||||
spinner.Success("Finished fetching ", src)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
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,
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
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,196 +0,0 @@
|
||||
package packager
|
||||
|
||||
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"
|
||||
"github.com/google/go-containerregistry/pkg/v1/remote"
|
||||
"github.com/google/go-containerregistry/pkg/v1/tarball"
|
||||
"github.com/mholt/archiver/v3"
|
||||
"github.com/rancherfederal/hauler/pkg/apis/hauler.cattle.io/v1alpha1"
|
||||
"github.com/rancherfederal/hauler/pkg/fetcher"
|
||||
"github.com/sirupsen/logrus"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Packager struct {
|
||||
Cluster *v1alpha1.Cluster
|
||||
logger *logrus.Entry
|
||||
za *archiver.TarZstd
|
||||
}
|
||||
|
||||
func NewPackager(cluster *v1alpha1.Cluster) *Packager {
|
||||
return &Packager{
|
||||
Cluster: cluster,
|
||||
logger: logrus.WithFields(logrus.Fields{
|
||||
"cluster": cluster.Metadata.Name,
|
||||
"driver": cluster.Driver.String(),
|
||||
}),
|
||||
za: &archiver.TarZstd{
|
||||
Tar: &archiver.Tar{
|
||||
OverwriteExisting: true,
|
||||
MkdirAll: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Packager) Package(ctx context.Context, outFile string) error {
|
||||
ctx, cancel := context.WithTimeout(ctx, 2*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
tmpdir, err := os.MkdirTemp("", "hauler")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
logrus.Debugf("Using temporary packaging directory: %s", tmpdir)
|
||||
|
||||
if err := p.prepFS(tmpdir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Build api space for providing git repos to package, below is just an example of it "working"
|
||||
//var reposToGet []string
|
||||
//reposToGet = append(reposToGet, []string{"https://github.com/hashicorp/go-getter.git", "https://github.com/open-policy-agent/conftest.git"}...)
|
||||
//
|
||||
//gitReposPath := filepath.Join(tmpdir, "repos")
|
||||
//if err := p.pkgGitRepos(ctx, gitReposPath, reposToGet); err != nil {
|
||||
// return err
|
||||
//}
|
||||
|
||||
var urlsToGet []string
|
||||
urlsToGet = append(urlsToGet, buildDriverURLs(p.Cluster.Driver)...)
|
||||
// TODO: User defined gets
|
||||
|
||||
filesPath := filepath.Join(tmpdir, v1alpha1.HaulerBin)
|
||||
if err := p.pkgFiles(ctx, filesPath, urlsToGet); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var imgs []string
|
||||
imgs = append(imgs, p.Cluster.Driver.Images()...)
|
||||
imgs = append(imgs, p.Cluster.PreloadImages...)
|
||||
// TODO: User defined preloaded images
|
||||
|
||||
preloadImagesPath := filepath.Join(tmpdir, v1alpha1.ImagePreloadPath(p.Cluster.Driver), "hauler.tar")
|
||||
if err := p.pkgPreloadImages(ctx, preloadImagesPath, imgs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = p.archive(ctx, tmpdir, outFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Packager) pkgFiles(ctx context.Context, dir string, rawUrls []string) error {
|
||||
logrus.Infof("Packaging %s driver artifacts...", p.Cluster.Driver.String())
|
||||
|
||||
f := fetcher.FileFetcher{}
|
||||
for _, rawUrl := range rawUrls {
|
||||
dst := filepath.Join(dir, fetcher.GetFileNameFromURL(rawUrl))
|
||||
|
||||
err := f.Get(ctx, rawUrl, dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildDriverURLs(driver v1alpha1.Driver) []string {
|
||||
urls := make([]string, 0)
|
||||
urls = append(urls, driver.ExecutableURL())
|
||||
|
||||
return urls
|
||||
}
|
||||
|
||||
func (p *Packager) pkgPreloadImages(ctx context.Context, dir string, images []string) error {
|
||||
logrus.Infof("Packaging %d images to preload...", len(images))
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
imageRefs := make(map[name.Reference]v1.Image)
|
||||
|
||||
for _, i := range images {
|
||||
ref, err := name.ParseReference(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.logger.Debugf("adding %s to preload image archive", ref.Name())
|
||||
img, err := remote.Image(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
imageRefs[ref] = img
|
||||
}
|
||||
|
||||
// Get preload images and save to a single compressed tarball
|
||||
if err := tarball.MultiRefWriteToFile(dir, imageRefs); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Packager) pkgGitRepos(ctx context.Context, dir string, repos []string) error {
|
||||
logrus.Infof("Packaging %d repos to prepopulate...", len(repos))
|
||||
|
||||
f := fetcher.GitFetcher{ Bare: true }
|
||||
for _, repo := range repos {
|
||||
dst := filepath.Join(dir, fetcher.GetFileNameFromURL(repo))
|
||||
|
||||
err := f.Get(ctx, repo, dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// archive will archive a folder using Packager's archiver while preserving the root folder structure
|
||||
func (p *Packager) archive(ctx context.Context, dir string, outFile string) error {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.Chdir(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Chdir(cwd)
|
||||
|
||||
|
||||
archivePath := filepath.Join(cwd, outFile)
|
||||
p.logger.Infof("Archiving contents of %s to %s", dir, archivePath)
|
||||
if err := p.za.Archive([]string{"."}, archivePath); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Packager) prepFS(dir string) error {
|
||||
for _, fs := range v1alpha1.FS(p.Cluster.Driver) {
|
||||
path := filepath.Join(dir, fs)
|
||||
|
||||
p.logger.Debugf("creating directory: %s", path)
|
||||
err := os.MkdirAll(path, 0755)
|
||||
if err != nil {
|
||||
return fmt.Errorf("to create directory: %s, %w", path, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/mholt/archiver/v3"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type dir struct {
|
||||
Path string
|
||||
Permission os.FileMode
|
||||
}
|
||||
|
||||
type FSLayout struct {
|
||||
Root string
|
||||
dirs []dir
|
||||
}
|
||||
|
||||
type Layout interface {
|
||||
Create() error
|
||||
AddDir()
|
||||
Archive(archiver2 archiver.Archiver) error
|
||||
Remove() error
|
||||
}
|
||||
|
||||
func NewLayout(root string) *FSLayout {
|
||||
absRoot, _ := filepath.Abs(root)
|
||||
return &FSLayout{
|
||||
Root: absRoot,
|
||||
dirs: nil,
|
||||
}
|
||||
}
|
||||
|
||||
//Create will create the FSLayout at the FSLayout.Root
|
||||
func (l FSLayout) Create() error {
|
||||
for _, dir := range l.dirs {
|
||||
fullPath := filepath.Join(l.Root, dir.Path)
|
||||
if err := os.MkdirAll(fullPath, dir.Permission); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//AddDir will add a folder to the FSLayout
|
||||
func (l *FSLayout) AddDir(relPath string, perm os.FileMode) {
|
||||
l.dirs = append(l.dirs, dir{
|
||||
Path: relPath,
|
||||
Permission: perm,
|
||||
})
|
||||
}
|
||||
|
||||
func (l FSLayout) Remove() error {
|
||||
return os.RemoveAll(l.Root)
|
||||
}
|
||||
|
||||
func (l FSLayout) Archive(a *archiver.TarZstd, name string) error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.Chdir(l.Root)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Chdir(cwd)
|
||||
|
||||
archiveFile := filepath.Join(cwd, fmt.Sprintf("%s.%s", name, a.String()))
|
||||
if err := a.Archive([]string{"."}, archiveFile); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
Vendored
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
registry:2.7.1
|
||||
gitea/gitea:1.14.1-rootless
|
||||
Reference in New Issue
Block a user