mirror of https://github.com/fluxcd/flux2.git
Add artifact commands
Implement build, push, pull and tag artifact commands. For authentication purposes, all `flux <verb> artifact` commands are using the '~/.docker/config.json' config file and the Docker credential helpers. Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>pull/2971/head
parent
e927d39a27
commit
9503ecafb1
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2022 The Flux authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/internal/oci"
|
||||||
|
)
|
||||||
|
|
||||||
|
var buildArtifactCmd = &cobra.Command{
|
||||||
|
Use: "artifact",
|
||||||
|
Short: "Build artifact",
|
||||||
|
Long: `The build artifact command creates an tgz file with the manifests from the given directory.`,
|
||||||
|
Example: `# Build the given manifests directory into an artifact
|
||||||
|
flux build artifact --path ./path/to/local/manifests --output ./path/to/artifact.tgz
|
||||||
|
`,
|
||||||
|
RunE: buildArtifactCmdRun,
|
||||||
|
}
|
||||||
|
|
||||||
|
type buildArtifactFlags struct {
|
||||||
|
output string
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
var buildArtifactArgs buildArtifactFlags
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
buildArtifactCmd.Flags().StringVar(&buildArtifactArgs.path, "path", "", "Path to the directory where the Kubernetes manifests are located.")
|
||||||
|
buildArtifactCmd.Flags().StringVarP(&buildArtifactArgs.output, "output", "0", "artifact.tgz", "Path to where the artifact tgz file should be written.")
|
||||||
|
buildCmd.AddCommand(buildArtifactCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildArtifactCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
|
if buildArtifactArgs.path == "" {
|
||||||
|
return fmt.Errorf("invalid path %q", buildArtifactArgs.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fs, err := os.Stat(buildArtifactArgs.path); err != nil || !fs.IsDir() {
|
||||||
|
return fmt.Errorf("invalid path %q", buildArtifactArgs.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Actionf("building artifact from %s", buildArtifactArgs.path)
|
||||||
|
|
||||||
|
if err := oci.Build(buildArtifactArgs.output, buildArtifactArgs.path); err != nil {
|
||||||
|
return fmt.Errorf("bulding artifact failed, error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Successf("artifact created at %s", buildArtifactArgs.output)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2022 The Flux authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var pullCmd = &cobra.Command{
|
||||||
|
Use: "pull",
|
||||||
|
Short: "Pull artifacts",
|
||||||
|
Long: "The pull command is used to download OCI artifacts.",
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(pullCmd)
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2022 The Flux authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/internal/oci"
|
||||||
|
)
|
||||||
|
|
||||||
|
var pullArtifactCmd = &cobra.Command{
|
||||||
|
Use: "artifact",
|
||||||
|
Short: "Push artifact",
|
||||||
|
Long: `The pull artifact command downloads and extracts the OCI artifact content to the given path.
|
||||||
|
The pull command uses the credentials from '~/.docker/config.json'.`,
|
||||||
|
Example: `# Pull an OCI artifact created by flux from GHCR
|
||||||
|
flux pull artifact ghcr.io/org/manifests/app:v0.0.1 --output ./path/to/local/manifests
|
||||||
|
`,
|
||||||
|
RunE: pullArtifactCmdRun,
|
||||||
|
}
|
||||||
|
|
||||||
|
type pullArtifactFlags struct {
|
||||||
|
output string
|
||||||
|
}
|
||||||
|
|
||||||
|
var pullArtifactArgs pullArtifactFlags
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
pullArtifactCmd.Flags().StringVarP(&pullArtifactArgs.output, "output", "o", "", "Path where the artifact content should be extracted.")
|
||||||
|
pullCmd.AddCommand(pullArtifactCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func pullArtifactCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
|
if len(args) < 1 {
|
||||||
|
return fmt.Errorf("artifact name is required")
|
||||||
|
}
|
||||||
|
url := args[0]
|
||||||
|
|
||||||
|
if pullArtifactArgs.output == "" {
|
||||||
|
return fmt.Errorf("invalid output path %s", pullArtifactArgs.output)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fs, err := os.Stat(pullArtifactArgs.output); err != nil || !fs.IsDir() {
|
||||||
|
return fmt.Errorf("invalid output path %s", pullArtifactArgs.output)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
logger.Actionf("pulling artifact from %s", url)
|
||||||
|
|
||||||
|
meta, err := oci.Pull(ctx, url, pullArtifactArgs.output)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Successf("source %s", meta.Source)
|
||||||
|
logger.Successf("revision %s", meta.Revision)
|
||||||
|
logger.Successf("digest %s", meta.Digest)
|
||||||
|
logger.Successf("artifact content extracted to %s", pullArtifactArgs.output)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 The Flux authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var pushCmd = &cobra.Command{
|
||||||
|
Use: "push",
|
||||||
|
Short: "Push artifacts",
|
||||||
|
Long: "The push command is used to publish OCI artifacts.",
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(pushCmd)
|
||||||
|
}
|
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2022 The Flux authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/internal/oci"
|
||||||
|
)
|
||||||
|
|
||||||
|
var pushArtifactCmd = &cobra.Command{
|
||||||
|
Use: "artifact",
|
||||||
|
Short: "Push artifact",
|
||||||
|
Long: `The push artifact command creates a tarball from the given directory and uploads the artifact to a OCI repository.
|
||||||
|
The push command uses the credentials from '~/.docker/config.json'.`,
|
||||||
|
Example: `# Push the local manifests to GHCR
|
||||||
|
flux push artifact ghcr.io/org/manifests/app:v0.0.1 \
|
||||||
|
--path="./path/to/local/manifests" \
|
||||||
|
--source="$(git config --get remote.origin.url)" \
|
||||||
|
--revision="$(git branch --show-current)/$(git rev-parse HEAD)"
|
||||||
|
`,
|
||||||
|
RunE: pushArtifactCmdRun,
|
||||||
|
}
|
||||||
|
|
||||||
|
type pushArtifactFlags struct {
|
||||||
|
path string
|
||||||
|
source string
|
||||||
|
revision string
|
||||||
|
}
|
||||||
|
|
||||||
|
var pushArtifactArgs pushArtifactFlags
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
pushArtifactCmd.Flags().StringVar(&pushArtifactArgs.path, "path", "", "Path to the directory where the Kubernetes manifests are located.")
|
||||||
|
pushArtifactCmd.Flags().StringVar(&pushArtifactArgs.source, "source", "", "The source address, e.g. Git URL.")
|
||||||
|
pushArtifactCmd.Flags().StringVar(&pushArtifactArgs.revision, "revision", "", "The source revision in the format '<branch|tag>/<commit-sha>'")
|
||||||
|
pushCmd.AddCommand(pushArtifactCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func pushArtifactCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
|
if len(args) < 1 {
|
||||||
|
return fmt.Errorf("artifact name is required")
|
||||||
|
}
|
||||||
|
url := args[0]
|
||||||
|
|
||||||
|
if pushArtifactArgs.source == "" {
|
||||||
|
return fmt.Errorf("--source is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
if pushArtifactArgs.revision == "" {
|
||||||
|
return fmt.Errorf("--revision is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
if pushArtifactArgs.path == "" {
|
||||||
|
return fmt.Errorf("invalid path %q", pushArtifactArgs.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fs, err := os.Stat(pushArtifactArgs.path); err != nil || !fs.IsDir() {
|
||||||
|
return fmt.Errorf("invalid path %q", pushArtifactArgs.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
meta := oci.Metadata{
|
||||||
|
Source: pushArtifactArgs.source,
|
||||||
|
Revision: pushArtifactArgs.revision,
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
logger.Actionf("pushing artifact to %s", url)
|
||||||
|
|
||||||
|
digest, err := oci.Push(ctx, url, pushArtifactArgs.path, meta)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("pushing artifact failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Successf("artifact successfully pushed to %s", digest)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 The Flux authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var tagCmd = &cobra.Command{
|
||||||
|
Use: "tag",
|
||||||
|
Short: "Tag artifacts",
|
||||||
|
Long: "The tag command is used to tag OCI artifacts.",
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(tagCmd)
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2022 The Flux authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/fluxcd/flux2/internal/oci"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var tagArtifactCmd = &cobra.Command{
|
||||||
|
Use: "artifact",
|
||||||
|
Short: "Tag artifact",
|
||||||
|
Long: `The tag artifact command creates tags for the given OCI artifact.
|
||||||
|
The tag command uses the credentials from '~/.docker/config.json'.`,
|
||||||
|
Example: `# Tag an artifact version as latest
|
||||||
|
flux tag artifact ghcr.io/org/manifests/app:v0.0.1 --tag latest
|
||||||
|
`,
|
||||||
|
RunE: tagArtifactCmdRun,
|
||||||
|
}
|
||||||
|
|
||||||
|
type tagArtifactFlags struct {
|
||||||
|
tags []string
|
||||||
|
}
|
||||||
|
|
||||||
|
var tagArtifactArgs tagArtifactFlags
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
tagArtifactCmd.Flags().StringSliceVar(&tagArtifactArgs.tags, "tag", nil, "Tag name.")
|
||||||
|
tagCmd.AddCommand(tagArtifactCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func tagArtifactCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
|
if len(args) < 1 {
|
||||||
|
return fmt.Errorf("artifact name is required")
|
||||||
|
}
|
||||||
|
url := args[0]
|
||||||
|
|
||||||
|
if len(tagArtifactArgs.tags) < 1 {
|
||||||
|
return fmt.Errorf("--tag is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
logger.Actionf("tagging artifact")
|
||||||
|
|
||||||
|
for _, tag := range tagArtifactArgs.tags {
|
||||||
|
img, err := oci.Tag(ctx, url, tag)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("tagging artifact failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Successf("artifact tagged as %s", img)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue