diff --git a/.github/workflows/testdata.yaml b/.github/workflows/testdata.yaml index 4aee2f8..e11f0f7 100644 --- a/.github/workflows/testdata.yaml +++ b/.github/workflows/testdata.yaml @@ -24,13 +24,14 @@ jobs: hauler login ghcr.io --username ${{ github.repository_owner }} --password ${{ secrets.GITHUB_TOKEN }} hauler login docker.io --username ${{ secrets.DOCKERHUB_USERNAME }} --password ${{ secrets.DOCKERHUB_TOKEN }} - - name: Process Testdata Manifests + - name: Process Images for Tests run: | - for manifest in testdata/*.yaml; do - echo "Processing $manifest..." - name=$(basename "$manifest" .yaml) - hauler store sync --filename "$manifest" - done + hauler store add image nginx:1.25-alpine + hauler store add image nginx:1.26-alpine + hauler store add image busybox + hauler store add image busybox:stable + hauler store add image gcr.io/distroless/base + hauler store add image gcr.io/distroless/base@sha256:7fa7445dfbebae4f4b7ab0e6ef99276e96075ae42584af6286ba080750d6dfe5 - name: Push Store Contents to Hauler-Dev GitHub Container Registry run: | diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 579962e..832201b 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -166,7 +166,7 @@ jobs: ! hauler store info | grep '/custom-path/rancher:2.8.4' # confirm old tag used if not specified hauler store add chart rancher --repo https://releases.rancher.com/server-charts/stable --version 2.8.4 --rewrite /custom-path/rancher - # confirm tag + # confirm tag hauler store info | grep '2.8.4' - name: Verify - hauler store add file @@ -205,7 +205,7 @@ jobs: ! hauler store info | grep '/custom-path/busybox:latest' # confirm old tag used if not specified hauler store add image ghcr.io/hauler-dev/library/busybox:stable --rewrite /custom-path/busybox - # confirm tag + # confirm tag hauler store info | grep ':stable' - name: Verify - hauler store copy @@ -354,12 +354,12 @@ jobs: # verify fileserver directory structure tree -hC fileserver - - name: Verify - hauler store delete-artifact (image) + - name: Verify - hauler store remove (image) run: | - hauler store delete-artifact --help + hauler store remove --help # add test images - hauler store add image docker.io/library/nginx:1.25-alpine - hauler store add image docker.io/library/nginx:1.26-alpine + hauler store add image ghcr.io/hauler-dev/library/nginx:1.25-alpine + hauler store add image ghcr.io/hauler-dev/library/nginx:1.26-alpine # confirm artifacts hauler store info | grep 'nginx:1.25' hauler store info | grep 'nginx:1.26' @@ -367,7 +367,7 @@ jobs: BLOBS_BEFORE=$(find store/blobs/sha256 -type f | wc -l | xargs) echo "blobs before deletion: $BLOBS_BEFORE" # delete one artifact - hauler store delete-artifact nginx:1.25 --force + hauler store remove nginx:1.25 --force # verify artifact removed ! hauler store info | grep -q "nginx:1.25" # non-deleted artifact exists @@ -384,12 +384,12 @@ jobs: echo "ERROR: All blobs deleted (shared layers removed)" exit 1 fi - # verify remaining image not missing layers - hauler store extract docker.io/library/nginx:1.26-alpine + # verify remaining image not missing layers + hauler store extract ghcr.io/hauler-dev/library/nginx:1.26-alpine - - name: Verify - hauler store delete-artifact (chart) + - name: Verify - hauler store remove (chart) run: | - hauler store delete-artifact --help + hauler store remove --help # add test images hauler store add chart rancher --repo https://releases.rancher.com/server-charts/stable --version 2.8.4 hauler store add chart rancher --repo https://releases.rancher.com/server-charts/stable --version 2.8.5 @@ -400,7 +400,7 @@ jobs: BLOBS_BEFORE=$(find store/blobs/sha256 -type f | wc -l | xargs) echo "blobs before deletion: $BLOBS_BEFORE" # delete one artifact - hauler store delete-artifact 2.8.4 --force + hauler store remove 2.8.4 --force # verify artifact removed ! hauler store info | grep -q "2.8.4" # non-deleted artifact exists @@ -417,12 +417,12 @@ jobs: echo "ERROR: All blobs deleted (shared layers removed)" exit 1 fi - # verify remaining image not missing layers + # verify remaining image not missing layers hauler store extract hauler/rancher:2.8.5 - - name: Verify - hauler store delete-artifact (file) + - name: Verify - hauler store remove (file) run: | - hauler store delete-artifact --help + hauler store remove --help # add test images hauler store add file https://get.hauler.dev hauler store add file https://get.rke2.io/install.sh @@ -433,7 +433,7 @@ jobs: BLOBS_BEFORE=$(find store/blobs/sha256 -type f | wc -l | xargs) echo "blobs before deletion: $BLOBS_BEFORE" # delete one artifact - hauler store delete-artifact get.hauler.dev --force + hauler store remove get.hauler.dev --force # verify artifact removed ! hauler store info | grep -q "get.hauler.dev" # non-deleted artifact exists @@ -450,7 +450,7 @@ jobs: echo "ERROR: All blobs deleted (shared layers removed)" exit 1 fi - # verify remaining image not missing layers + # verify remaining image not missing layers hauler store extract hauler/install.sh:latest - name: Create Hauler Report diff --git a/README.md b/README.md index 91f6e25..23dc051 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,19 @@ For more information, please review the **[Hauler Documentation](https://hauler. ## Recent Changes +### In Hauler v1.4.0... + +- Added a notice to `hauler store sync --products/--product-registry` to warn users the default registry will be updated in a future release. + - Users will see logging notices when using the `--products/--product-registry` such as... + - `!!! WARNING !!! [--products] will be updating its default registry in a future release...` + - `!!! WARNING !!! [--product-registry] will be updating its default registry in a future release...` + ### In Hauler v1.2.0... - Upgraded the `apiVersion` to `v1` from `v1alpha1` - Users are able to use `v1` and `v1alpha1`, but `v1alpha1` is now deprecated and will be removed in a future release. We will update the community when we fully deprecate and remove the functionality of `v1alpha1` - Users will see logging notices when using the old `apiVersion` such as... - - `!!! DEPRECATION WARNING !!! apiVersion [v1alpha1] will be removed in a future release !!! DEPRECATION WARNING !!!` + - `!!! DEPRECATION WARNING !!! apiVersion [v1alpha1] will be removed in a future release...` --- - Updated the behavior of `hauler store load` to default to loading a `haul` with the name of `haul.tar.zst` and requires the flag of `--filename/-f` to load a `haul` with a different name - Users can load multiple `hauls` by specifying multiple flags of `--filename/-f` diff --git a/cmd/hauler/cli/cli.go b/cmd/hauler/cli/cli.go index d168db2..3cb68b8 100644 --- a/cmd/hauler/cli/cli.go +++ b/cmd/hauler/cli/cli.go @@ -14,7 +14,7 @@ func New(ctx context.Context, ro *flags.CliRootOpts) *cobra.Command { cmd := &cobra.Command{ Use: "hauler", Short: "Airgap Swiss Army Knife", - Example: " View the Docs: https://docs.hauler.dev\n Environment Variables: " + consts.HaulerDir + " | " + consts.HaulerTempDir + " | " + consts.HaulerStoreDir + " | " + consts.HaulerIgnoreErrors, + Example: " View the Docs: https://docs.hauler.dev\n Environment Variables: " + consts.HaulerDir + " | " + consts.HaulerTempDir + " | " + consts.HaulerStoreDir + " | " + consts.HaulerIgnoreErrors + "\n Warnings: Hauler commands and flags marked with (EXPERIMENTAL) are not yet stable and may change in the future.", PersistentPreRunE: func(cmd *cobra.Command, args []string) error { l := log.FromContext(ctx) l.SetLevel(ro.LogLevel) diff --git a/cmd/hauler/cli/store.go b/cmd/hauler/cli/store.go index b94995e..6a8dcf3 100644 --- a/cmd/hauler/cli/store.go +++ b/cmd/hauler/cli/store.go @@ -8,6 +8,7 @@ import ( "hauler.dev/go/hauler/cmd/hauler/cli/store" "hauler.dev/go/hauler/internal/flags" + "hauler.dev/go/hauler/pkg/log" ) func addStore(parent *cobra.Command, ro *flags.CliRootOpts) { @@ -32,7 +33,7 @@ func addStore(parent *cobra.Command, ro *flags.CliRootOpts) { addStoreInfo(rso, ro), addStoreCopy(rso, ro), addStoreAdd(rso, ro), - addStoreDeleteArtifact(rso, ro), + addStoreRemove(rso, ro), ) parent.AddCommand(cmd) @@ -70,9 +71,16 @@ func addStoreSync(rso *flags.StoreRootOpts, ro *flags.CliRootOpts) *cobra.Comman Short: "Sync content to the content store", Args: cobra.ExactArgs(0), PreRunE: func(cmd *cobra.Command, args []string) error { - // Check if the products flag was passed + // warn if products or product-registry flag is used by the user + if cmd.Flags().Changed("products") { + log.FromContext(cmd.Context()).Warnf("!!! WARNING !!! [--products] will be updating its default registry in a future release.") + } + if cmd.Flags().Changed("product-registry") { + log.FromContext(cmd.Context()).Warnf("!!! WARNING !!! [--product-registry] will be updating its default registry in a future release.") + } + // check if the products flag was passed if len(o.Products) > 0 { - // Only clear the default if the user did NOT explicitly set --filename + // only clear the default if the user did not explicitly set it if !cmd.Flags().Changed("filename") { o.FileName = []string{} } @@ -328,7 +336,10 @@ hauler store add image gcr.io/distroless/base@sha256:7fa7445dfbebae4f4b7ab0e6ef9 # fetch image with full image reference, specific platform, and signature verification curl -sfOL https://raw.githubusercontent.com/rancherfederal/carbide-releases/main/carbide-key.pub -hauler store add image rgcrprod.azurecr.us/rancher/rke2-runtime:v1.31.5-rke2r1 --platform linux/amd64 --key carbide-key.pub`, +hauler store add image rgcrprod.azurecr.us/rancher/rke2-runtime:v1.31.5-rke2r1 --platform linux/amd64 --key carbide-key.pub + +# fetch image and rewrite path +hauler store add image busybox --rewrite custom-path/busybox:latest`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() @@ -368,7 +379,10 @@ hauler store add chart hauler-helm --repo oci://ghcr.io/hauler-dev --version 1.2 hauler store add chart rancher --repo https://releases.rancher.com/server-charts/stable # fetch remote helm chart with specific version -hauler store add chart rancher --repo https://releases.rancher.com/server-charts/latest --version 2.10.1`, +hauler store add chart rancher --repo https://releases.rancher.com/server-charts/latest --version 2.10.1 + +# fetch remote helm chart and rewrite path +hauler store add chart hauler-helm --repo oci://ghcr.io/hauler-dev --rewrite custom-path/hauler-chart:latest`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() @@ -386,13 +400,32 @@ hauler store add chart rancher --repo https://releases.rancher.com/server-charts return cmd } -func addStoreDeleteArtifact(rso *flags.StoreRootOpts, ro *flags.CliRootOpts) *cobra.Command { - o := &flags.DeleteArtifactOpts{} +func addStoreRemove(rso *flags.StoreRootOpts, ro *flags.CliRootOpts) *cobra.Command { + o := &flags.RemoveOpts{} cmd := &cobra.Command{ - Use: "delete-artifact ", - Short: "Delete an artifact from the content store (experimental)", - Aliases: []string{"del"}, - Args: cobra.ExactArgs(1), + Use: "remove ", + Short: "Remove an artifact from the content store (experimental)", + Example: `# remove an image using full store reference +hauler store info +hauler store remove index.docker.io/library/busybox:stable + +# remove a chart using full store reference +hauler store info +hauler store remove hauler/rancher:2.8.4 + +# remove a file using full store reference +hauler store info +hauler store remove hauler/rke2-install.sh + +# remove any artifact with the latest tag +hauler store remove :latest + +# remove any artifact with 'busybox' in the reference +hauler store remove busybox + +# force remove without verification +hauler store remove busybox:latest --force`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() @@ -401,7 +434,7 @@ func addStoreDeleteArtifact(rso *flags.StoreRootOpts, ro *flags.CliRootOpts) *co return err } - return store.DeleteArtifactCmd(ctx, o, s, args[0]) + return store.RemoveCmd(ctx, o, s, args[0]) }, } o.AddFlags(cmd) diff --git a/cmd/hauler/cli/store/delete-artifact.go b/cmd/hauler/cli/store/remove.go similarity index 85% rename from cmd/hauler/cli/store/delete-artifact.go rename to cmd/hauler/cli/store/remove.go index 357e5bb..33b820e 100644 --- a/cmd/hauler/cli/store/delete-artifact.go +++ b/cmd/hauler/cli/store/remove.go @@ -12,7 +12,7 @@ import ( "hauler.dev/go/hauler/pkg/store" ) -func DeleteArtifactCmd(ctx context.Context, o *flags.DeleteArtifactOpts, s *store.Layout, ref string) error { +func RemoveCmd(ctx context.Context, o *flags.RemoveOpts, s *store.Layout, ref string) error { l := log.FromContext(ctx) // collect matching artifacts @@ -67,13 +67,13 @@ func DeleteArtifactCmd(ctx context.Context, o *flags.DeleteArtifactOpts, s *stor } } - //delete artifact(s) + //remove artifact(s) for _, m := range matches { - if err := s.DeleteArtifact(ctx, m.reference, m.desc); err != nil { - return fmt.Errorf("failed to delete artifact %s: %w", m.reference, err) + if err := s.RemoveArtifact(ctx, m.reference, m.desc); err != nil { + return fmt.Errorf("failed to remove artifact %s: %w", m.reference, err) } - l.Infof("deleted [%s] of type %s with digest [%s]", m.reference, m.desc.MediaType, m.desc.Digest.String()) + l.Infof("removed [%s] of type %s with digest [%s]", m.reference, m.desc.MediaType, m.desc.Digest.String()) } // clean up unreferenced blobs diff --git a/cmd/hauler/cli/store/sync.go b/cmd/hauler/cli/store/sync.go index efdbee2..99eb120 100644 --- a/cmd/hauler/cli/store/sync.go +++ b/cmd/hauler/cli/store/sync.go @@ -169,7 +169,7 @@ func processContent(ctx context.Context, fi *os.File, o *flags.SyncOpts, s *stor case consts.FilesContentKind: switch gvk.Version { case "v1alpha1": - l.Warnf("!!! DEPRECATION WARNING !!! apiVersion [%s] will be removed in a future release !!! DEPRECATION WARNING !!!", gvk.Version) + l.Warnf("!!! DEPRECATION WARNING !!! apiVersion [%s] will be removed in a future release...", gvk.Version) var alphaCfg v1alpha1.Files if err := yaml.Unmarshal(doc, &alphaCfg); err != nil { @@ -203,7 +203,7 @@ func processContent(ctx context.Context, fi *os.File, o *flags.SyncOpts, s *stor case consts.ImagesContentKind: switch gvk.Version { case "v1alpha1": - l.Warnf("!!! DEPRECATION WARNING !!! apiVersion [%s] will be removed in a future release !!! DEPRECATION WARNING !!!", gvk.Version) + l.Warnf("!!! DEPRECATION WARNING !!! apiVersion [%s] will be removed in a future release...", gvk.Version) var alphaCfg v1alpha1.Images if err := yaml.Unmarshal(doc, &alphaCfg); err != nil { @@ -496,7 +496,7 @@ func processContent(ctx context.Context, fi *os.File, o *flags.SyncOpts, s *stor case consts.ChartsContentKind: switch gvk.Version { case "v1alpha1": - l.Warnf("!!! DEPRECATION WARNING !!! apiVersion [%s] will be removed in a future release !!! DEPRECATION WARNING !!!", gvk.Version) + l.Warnf("!!! DEPRECATION WARNING !!! apiVersion [%s] will be removed in a future release...", gvk.Version) var alphaCfg v1alpha1.Charts if err := yaml.Unmarshal(doc, &alphaCfg); err != nil { @@ -530,7 +530,7 @@ func processContent(ctx context.Context, fi *os.File, o *flags.SyncOpts, s *stor case consts.ChartsCollectionKind: switch gvk.Version { case "v1alpha1": - l.Warnf("!!! DEPRECATION WARNING !!! apiVersion [%s] will be removed in a future release !!! DEPRECATION WARNING !!!", gvk.Version) + l.Warnf("!!! DEPRECATION WARNING !!! apiVersion [%s] will be removed in a future release...", gvk.Version) var alphaCfg v1alpha1.ThickCharts if err := yaml.Unmarshal(doc, &alphaCfg); err != nil { @@ -578,7 +578,7 @@ func processContent(ctx context.Context, fi *os.File, o *flags.SyncOpts, s *stor case consts.ImageTxtsContentKind: switch gvk.Version { case "v1alpha1": - l.Warnf("!!! DEPRECATION WARNING !!! apiVersion [%s] will be removed in a future release !!! DEPRECATION WARNING !!!", gvk.Version) + l.Warnf("!!! DEPRECATION WARNING !!! apiVersion [%s] will be removed in a future release...", gvk.Version) var alphaCfg v1alpha1.ImageTxts if err := yaml.Unmarshal(doc, &alphaCfg); err != nil { diff --git a/internal/flags/add.go b/internal/flags/add.go index 28b55db..c442ff6 100644 --- a/internal/flags/add.go +++ b/internal/flags/add.go @@ -61,5 +61,8 @@ func (o *AddChartOpts) AddFlags(cmd *cobra.Command) { f.StringVar(&o.ChartOpts.KeyFile, "key-file", "", "(Optional) Location of the TLS Key to use for client authenication") f.BoolVar(&o.ChartOpts.InsecureSkipTLSverify, "insecure-skip-tls-verify", false, "(Optional) Skip TLS certificate verification") f.StringVar(&o.ChartOpts.CaFile, "ca-file", "", "(Optional) Location of CA Bundle to enable certification verification") - f.StringVar(&o.Rewrite, "rewrite", "", "(Optional) Rewrite artifact path to specified string (experimental)") + f.StringVar(&o.Rewrite, "rewrite", "", "(Optional) Rewrite artifact path to specified string (EXPERIMENTAL)") + + cmd.MarkFlagsRequiredTogether("username", "password") + cmd.MarkFlagsRequiredTogether("cert-file", "key-file", "ca-file") } diff --git a/internal/flags/copy.go b/internal/flags/copy.go index ad11e36..835cf1a 100644 --- a/internal/flags/copy.go +++ b/internal/flags/copy.go @@ -21,6 +21,8 @@ func (o *CopyOpts) AddFlags(cmd *cobra.Command) { f.BoolVar(&o.PlainHTTP, "plain-http", false, "(Optional) Allow plain HTTP connections") f.StringVarP(&o.Only, "only", "o", "", "(Optional) Custom string array to only copy specific 'image' items") + cmd.MarkFlagsRequiredTogether("username", "password") + if err := f.MarkDeprecated("username", "please use 'hauler login'"); err != nil { panic(err) } diff --git a/internal/flags/delete-artifact.go b/internal/flags/delete-artifact.go deleted file mode 100644 index ff37be0..0000000 --- a/internal/flags/delete-artifact.go +++ /dev/null @@ -1,11 +0,0 @@ -package flags - -import "github.com/spf13/cobra" - -type DeleteArtifactOpts struct { - Force bool // skip delete confirmation -} - -func (o *DeleteArtifactOpts) AddFlags(cmd *cobra.Command) { - cmd.Flags().BoolVarP(&o.Force, "force", "f", false, "(Optional) Delete artifacts without confirmation") -} diff --git a/internal/flags/remove.go b/internal/flags/remove.go new file mode 100644 index 0000000..3c08ad7 --- /dev/null +++ b/internal/flags/remove.go @@ -0,0 +1,11 @@ +package flags + +import "github.com/spf13/cobra" + +type RemoveOpts struct { + Force bool // skip remove confirmation +} + +func (o *RemoveOpts) AddFlags(cmd *cobra.Command) { + cmd.Flags().BoolVarP(&o.Force, "force", "f", false, "(Optional) Remove artifact(s) without confirmation") +} diff --git a/pkg/store/store.go b/pkg/store/store.go index 0f799a4..cef607a 100644 --- a/pkg/store/store.go +++ b/pkg/store/store.go @@ -262,8 +262,8 @@ func (l *Layout) writeLayer(layer v1.Layer) error { return err } -// Delete artifact reference from the store -func (l *Layout) DeleteArtifact(ctx context.Context, reference string, desc ocispec.Descriptor) error { +// Remove artifact reference from the store +func (l *Layout) RemoveArtifact(ctx context.Context, reference string, desc ocispec.Descriptor) error { if err := l.OCI.LoadIndex(); err != nil { return err }