mirror of
https://github.com/labring/sealos.git
synced 2026-09-01 04:58:47 +08:00
feat: run sealos without root privileges (#2163)
* refactor: migrate buildah's subcommands Signed-off-by: fengxsong <fengxsong@outlook.com> * feat: rootless mode(almost done) Signed-off-by: fengxsong <fengxsong@outlook.com> * fix: set default values for --platform Signed-off-by: fengxsong <fengxsong@outlook.com> * fix: meet ci requirements Signed-off-by: fengxsong <fengxsong@outlook.com> * ci: add build-deps Signed-off-by: fengxsong <fengxsong@outlook.com> * style: define constants Signed-off-by: fengxsong <fengxsong@outlook.com> * feat: provide function to register default appliers Signed-off-by: fengxsong <fengxsong@outlook.com> * style: fix short descriptions of all subcommands Signed-off-by: fengxsong <fengxsong@outlook.com> Signed-off-by: fengxsong <fengxsong@outlook.com>
This commit is contained in:
+14
-14
@@ -7,13 +7,13 @@ env:
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
branches: ["main"]
|
||||
paths-ignore:
|
||||
- "docs/**"
|
||||
- "**/*.md"
|
||||
- "**/*.yaml"
|
||||
pull_request:
|
||||
branches: [ "*" ]
|
||||
branches: ["*"]
|
||||
paths-ignore:
|
||||
- "docs/**"
|
||||
- "**/*.md"
|
||||
@@ -40,8 +40,8 @@ jobs:
|
||||
- name: Commit Changes
|
||||
uses: stefanzweifel/git-auto-commit-action@v4
|
||||
with:
|
||||
commit_message: 'chore(fmt): format code'
|
||||
commit_options: '-s'
|
||||
commit_message: "chore(fmt): format code"
|
||||
commit_options: "-s"
|
||||
|
||||
check-license:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -72,7 +72,7 @@ jobs:
|
||||
run: sh ./scripts/resolve-modules.sh
|
||||
|
||||
golangci-lint:
|
||||
needs: [ format-code, check-license, resolve-modules ]
|
||||
needs: [format-code, check-license, resolve-modules]
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix: ${{ fromJson(needs.resolve-modules.outputs.matrix) }}
|
||||
@@ -96,7 +96,7 @@ jobs:
|
||||
args: "--out-${NO_FUTURE}format colored-line-number"
|
||||
|
||||
coverage:
|
||||
needs: [ format-code, check-license ]
|
||||
needs: [format-code, check-license]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -117,12 +117,12 @@ jobs:
|
||||
uses: codecov/codecov-action@v3
|
||||
|
||||
build:
|
||||
needs: [ golangci-lint, coverage ]
|
||||
needs: [golangci-lint, coverage]
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
binary: [ sealos, sealctl, lvscare, image-cri-shim ]
|
||||
arch: [ arm64, amd64 ]
|
||||
binary: [sealos, sealctl, lvscare, image-cri-shim]
|
||||
arch: [arm64, amd64]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
@@ -135,7 +135,7 @@ jobs:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Install Dependencies
|
||||
if: ${{ matrix.binary == 'sealos' && matrix.arch == 'arm64' }}
|
||||
if: ${{ (matrix.binary == 'sealos' || matrix.binary == 'sealctl') && matrix.arch == 'arm64' }}
|
||||
run: |
|
||||
sudo apt install -y gcc-aarch64-linux-gnu
|
||||
|
||||
@@ -154,7 +154,7 @@ jobs:
|
||||
path: bin/linux_${{ matrix.arch}}/${{ matrix.binary }}
|
||||
|
||||
docker:
|
||||
needs: [ build ]
|
||||
needs: [build]
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
registry:
|
||||
@@ -163,7 +163,7 @@ jobs:
|
||||
- 5000:5000
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [ arm64, amd64 ]
|
||||
arch: [arm64, amd64]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
@@ -237,7 +237,7 @@ jobs:
|
||||
failOnError: false
|
||||
|
||||
container:
|
||||
needs: [ docker ]
|
||||
needs: [docker]
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
steps:
|
||||
@@ -304,7 +304,7 @@ jobs:
|
||||
.
|
||||
|
||||
patch-oci:
|
||||
needs: [ docker ]
|
||||
needs: [docker]
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
steps:
|
||||
|
||||
@@ -17,12 +17,11 @@ package cmd
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/labring/sealos/pkg/utils/flags"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/labring/sealos/pkg/cert"
|
||||
"github.com/labring/sealos/pkg/utils/flags"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
)
|
||||
|
||||
func newCertCmd() *cobra.Command {
|
||||
|
||||
@@ -21,12 +21,12 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
utilsexec "k8s.io/utils/exec"
|
||||
|
||||
"github.com/labring/image-cri-shim/pkg/cri"
|
||||
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
utilsexec "k8s.io/utils/exec"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -17,9 +17,9 @@ package cmd
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
)
|
||||
|
||||
func newHostsNameCmd() *cobra.Command {
|
||||
|
||||
@@ -19,11 +19,11 @@ package cmd
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/labring/sealos/pkg/constants"
|
||||
"github.com/labring/sealos/pkg/utils/hosts"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var hostsPath string
|
||||
|
||||
@@ -14,8 +14,10 @@
|
||||
|
||||
package cmd
|
||||
|
||||
import "github.com/labring/sealos/pkg/image/cmd"
|
||||
import (
|
||||
"github.com/labring/sealos/pkg/buildah"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmd.Register(rootCmd, getContact())
|
||||
buildah.RegisterRootCommand(rootCmd)
|
||||
}
|
||||
|
||||
@@ -18,10 +18,10 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/labring/sealos/pkg/utils/exec"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newKubeCmd() *cobra.Command {
|
||||
|
||||
@@ -17,11 +17,10 @@ package cmd
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/labring/sealos/pkg/passwd"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
)
|
||||
|
||||
func newPasswordCmd() *cobra.Command {
|
||||
|
||||
@@ -19,9 +19,9 @@ package cmd
|
||||
import (
|
||||
"runtime"
|
||||
|
||||
"github.com/labring/sealos/pkg/registry/cmd"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/labring/sealos/pkg/registry/cmd"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -18,9 +18,9 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -19,13 +19,12 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/labring/sealos/pkg/constants"
|
||||
"github.com/labring/sealos/pkg/utils/file"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/labring/sealos/pkg/constants"
|
||||
"github.com/labring/sealos/pkg/ipvs"
|
||||
"github.com/labring/sealos/pkg/utils/file"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
)
|
||||
|
||||
var staticPodPath string
|
||||
|
||||
@@ -17,12 +17,11 @@ package cmd
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
|
||||
"github.com/labring/sealos/pkg/runtime"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/util/json"
|
||||
|
||||
"github.com/labring/sealos/pkg/runtime"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
)
|
||||
|
||||
func newTokenCmd() *cobra.Command {
|
||||
|
||||
@@ -18,11 +18,9 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/labring/sealos/pkg/constants"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/labring/sealos/pkg/version"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var shortPrint bool
|
||||
@@ -53,7 +51,3 @@ func newVersionCmd() *cobra.Command {
|
||||
func init() {
|
||||
rootCmd.AddCommand(newVersionCmd())
|
||||
}
|
||||
|
||||
func getContact() string {
|
||||
return fmt.Sprintf(constants.Contact, version.Get().String())
|
||||
}
|
||||
|
||||
@@ -15,9 +15,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/containers/buildah"
|
||||
|
||||
"github.com/labring/sealos/cmd/sealctl/cmd"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if buildah.InitReexec() {
|
||||
return
|
||||
}
|
||||
cmd.Execute()
|
||||
}
|
||||
|
||||
@@ -17,11 +17,10 @@ package cmd
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/labring/sealos/pkg/apply"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
)
|
||||
|
||||
const exampleAdd = `
|
||||
@@ -40,7 +39,7 @@ func newAddCmd() *cobra.Command {
|
||||
}
|
||||
var addCmd = &cobra.Command{
|
||||
Use: "add",
|
||||
Short: "add some nodes",
|
||||
Short: "Add nodes into cluster",
|
||||
Args: cobra.NoArgs,
|
||||
Example: exampleAdd,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
@@ -17,9 +17,8 @@ package cmd
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
|
||||
"github.com/labring/sealos/pkg/apply"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
)
|
||||
|
||||
var clusterFile string
|
||||
@@ -29,7 +28,7 @@ func newApplyCmd() *cobra.Command {
|
||||
// applyCmd represents the apply command
|
||||
var applyCmd = &cobra.Command{
|
||||
Use: "apply",
|
||||
Short: "apply a kubernetes cluster",
|
||||
Short: "Run cloud images within a kubernetes cluster with Clusterfile",
|
||||
Example: `sealos apply -f Clusterfile`,
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
@@ -17,12 +17,11 @@ package cmd
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/labring/sealos/pkg/apply"
|
||||
"github.com/labring/sealos/pkg/apply/processor"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
)
|
||||
|
||||
var exampleDelete = `
|
||||
@@ -49,7 +48,7 @@ func newDeleteCmd() *cobra.Command {
|
||||
}
|
||||
var deleteCmd = &cobra.Command{
|
||||
Use: "delete",
|
||||
Short: "delete some node",
|
||||
Short: "Remove nodes from cluster",
|
||||
Args: cobra.NoArgs,
|
||||
Example: exampleDelete,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
@@ -19,9 +19,8 @@ package cmd
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/labring/sealos/pkg/ssh"
|
||||
|
||||
"github.com/labring/sealos/pkg/clusterfile"
|
||||
"github.com/labring/sealos/pkg/ssh"
|
||||
"github.com/labring/sealos/pkg/types/v1beta1"
|
||||
)
|
||||
|
||||
@@ -44,7 +43,7 @@ func newExecCmd() *cobra.Command {
|
||||
var cluster *v1beta1.Cluster
|
||||
var execCmd = &cobra.Command{
|
||||
Use: "exec",
|
||||
Short: "exec a shell command or script on all node.",
|
||||
Short: "Execute shell command or script on specified nodes",
|
||||
Example: exampleExec,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
@@ -50,7 +50,7 @@ func newGenCmd() *cobra.Command {
|
||||
var out string
|
||||
var genCmd = &cobra.Command{
|
||||
Use: "gen",
|
||||
Short: "generate a Clusterfile",
|
||||
Short: "Generate a Clusterfile with all default settings",
|
||||
Long: `generate a Clusterfile of the kubernetes cluster, which can be applied by 'sealos apply' command`,
|
||||
Example: exampleGen,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/labring/sealos/pkg/image/cmd"
|
||||
"github.com/labring/sealos/pkg/buildah"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmd.Register(rootCmd, getContact())
|
||||
buildah.RegisterRootCommand(rootCmd)
|
||||
}
|
||||
|
||||
@@ -17,10 +17,9 @@ package cmd
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
|
||||
"github.com/labring/sealos/pkg/apply"
|
||||
"github.com/labring/sealos/pkg/apply/processor"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
)
|
||||
|
||||
var exampleReset = `
|
||||
@@ -36,8 +35,7 @@ func newResetCmd() *cobra.Command {
|
||||
|
||||
var resetCmd = &cobra.Command{
|
||||
Use: "reset",
|
||||
Short: "simplest way to reset your cluster",
|
||||
Long: `sealos reset --name [arg]`,
|
||||
Short: "Reset all, everything in the cluster",
|
||||
Example: exampleReset,
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
+10
-6
@@ -28,12 +28,13 @@ import (
|
||||
var (
|
||||
debug bool
|
||||
clusterRootDir string
|
||||
runtimeRootDir string
|
||||
)
|
||||
|
||||
// rootCmd represents the base command when called without any subcommands
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "sealos",
|
||||
Short: "simplest way install kubernetes tools.",
|
||||
Short: "sealos is a Kubernetes distribution, a unified OS to manage cloud native applications.",
|
||||
// Uncomment the following line if your bare application
|
||||
// has an action associated with it:
|
||||
// Run: func(cmd *cobra.Command, args []string) { },
|
||||
@@ -50,16 +51,19 @@ func Execute() {
|
||||
|
||||
func init() {
|
||||
cobra.OnInitialize(onBootOnDie)
|
||||
|
||||
rootCmd.PersistentFlags().BoolVar(&debug, "debug", false, "enable debug logger")
|
||||
rootCmd.PersistentFlags().StringVar(&clusterRootDir, "cluster-root", constants.DefaultClusterRootfsDir, "cluster root directory")
|
||||
fs := rootCmd.PersistentFlags()
|
||||
fs.BoolVar(&debug, "debug", false, "enable debug logger")
|
||||
fs.StringVar(&clusterRootDir, "cluster-root", constants.DefaultClusterRootFsDir, "cluster root directory for remote")
|
||||
_ = fs.MarkHidden("cluster-root")
|
||||
fs.StringVar(&runtimeRootDir, "sealos-root", constants.DefaultRuntimeRootDir, "root directory for sealos actions")
|
||||
_ = fs.MarkHidden("sealos-root")
|
||||
}
|
||||
|
||||
func onBootOnDie() {
|
||||
constants.DefaultClusterRootfsDir = clusterRootDir
|
||||
constants.DefaultClusterRootFsDir = clusterRootDir
|
||||
constants.DefaultRuntimeRootDir = runtimeRootDir
|
||||
var rootDirs = []string{
|
||||
constants.LogPath(),
|
||||
constants.DataPath(),
|
||||
constants.Workdir(),
|
||||
}
|
||||
if err := file.MkDirs(rootDirs...); err != nil {
|
||||
|
||||
@@ -20,8 +20,7 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/labring/sealos/pkg/image"
|
||||
"github.com/labring/sealos/pkg/image/types"
|
||||
"github.com/labring/sealos/pkg/buildah"
|
||||
"github.com/labring/sealos/pkg/utils/iputils"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
strings2 "github.com/labring/sealos/pkg/utils/strings"
|
||||
@@ -65,7 +64,7 @@ func newRunCmd() *cobra.Command {
|
||||
var runSingle bool
|
||||
var runCmd = &cobra.Command{
|
||||
Use: "run",
|
||||
Short: "simplest way to run your kubernetes HA cluster",
|
||||
Short: "Run cloud native applications with ease, with or without a existing cluster",
|
||||
Long: `sealos run labring/kubernetes:v1.24.0 --masters [arg] --nodes [arg]`,
|
||||
Example: exampleRun,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
@@ -86,8 +85,8 @@ func newRunCmd() *cobra.Command {
|
||||
return applier.Apply()
|
||||
},
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
if !strings2.In(types.DefaultTransport, []string{types.OCIArchive, types.DockerArchive}) {
|
||||
return fmt.Errorf("transport parameters must be %s or %s", types.OCIArchive, types.DockerArchive)
|
||||
if !strings2.In(buildah.DefaultTransport, []string{buildah.OCIArchive, buildah.DockerArchive}) {
|
||||
return fmt.Errorf("transport parameters must be %s or %s", buildah.OCIArchive, buildah.DockerArchive)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
@@ -98,7 +97,8 @@ func newRunCmd() *cobra.Command {
|
||||
runArgs.RegisterFlags(runCmd.Flags())
|
||||
runCmd.Flags().BoolVar(&runSingle, "single", false, "run cluster in single mode")
|
||||
runCmd.Flags().BoolVarP(&processor.ForceOverride, "force", "f", false, "force override app in this cluster")
|
||||
runCmd.Flags().StringVarP(&types.DefaultTransport, "transport", "t", types.OCIArchive, fmt.Sprintf("load image transport from tar archive file.(optional value: %s, %s)", types.OCIArchive, types.DockerArchive))
|
||||
runCmd.Flags().StringVarP(&buildah.DefaultTransport, "transport", "t", buildah.OCIArchive,
|
||||
fmt.Sprintf("load image transport from tar archive file.(optional value: %s, %s)", buildah.OCIArchive, buildah.DockerArchive))
|
||||
return runCmd
|
||||
}
|
||||
|
||||
@@ -108,13 +108,13 @@ func init() {
|
||||
|
||||
func args2Images(args []string) ([]string, error) {
|
||||
var images []string
|
||||
imageSvc, err := image.NewImageService()
|
||||
bder, err := buildah.New("")
|
||||
if err != nil {
|
||||
return images, err
|
||||
}
|
||||
for _, arg := range args {
|
||||
if strings.HasSuffix(arg, ".tar") || strings.HasSuffix(arg, ".gz") {
|
||||
id, err := imageSvc.Load(arg)
|
||||
id, err := bder.Load(arg)
|
||||
if err != nil {
|
||||
return images, err
|
||||
}
|
||||
|
||||
@@ -19,9 +19,8 @@ package cmd
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/labring/sealos/pkg/ssh"
|
||||
|
||||
"github.com/labring/sealos/pkg/clusterfile"
|
||||
"github.com/labring/sealos/pkg/ssh"
|
||||
"github.com/labring/sealos/pkg/types/v1beta1"
|
||||
)
|
||||
|
||||
@@ -44,9 +43,8 @@ set ips to copy file:
|
||||
func newScpCmd() *cobra.Command {
|
||||
var cluster *v1beta1.Cluster
|
||||
var scpCmd = &cobra.Command{
|
||||
Use: "scp",
|
||||
// Aliases: []string{"cp"},
|
||||
Short: "copy local file to remote on all node.",
|
||||
Use: "scp",
|
||||
Short: "Copy file to remote on specified nodes",
|
||||
Example: exampleScp,
|
||||
Args: cobra.ExactArgs(2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
@@ -30,7 +30,7 @@ var shortPrint bool
|
||||
func newVersionCmd() *cobra.Command {
|
||||
var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "version",
|
||||
Short: "Print version info",
|
||||
Args: cobra.NoArgs,
|
||||
Example: `sealos version`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
@@ -3,21 +3,21 @@ module github.com/labring/sealos
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.2.0
|
||||
github.com/BurntSushi/toml v1.2.1
|
||||
github.com/Masterminds/sprig/v3 v3.2.2
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.985
|
||||
github.com/casdoor/casdoor-go-sdk v0.4.1
|
||||
github.com/containers/buildah v1.26.1
|
||||
github.com/containers/common v0.48.0
|
||||
github.com/containers/image/v5 v5.22.0
|
||||
github.com/containers/buildah v1.28.1
|
||||
github.com/containers/common v0.50.1
|
||||
github.com/containers/image/v5 v5.23.0
|
||||
github.com/containers/ocicrypt v1.1.5
|
||||
github.com/containers/storage v1.42.0
|
||||
github.com/containers/storage v1.43.0
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/distribution/distribution/v3 v3.0.0-20220526142353-ffbd94cbe269
|
||||
github.com/docker/cli v20.10.17+incompatible
|
||||
github.com/docker/distribution v2.8.1+incompatible
|
||||
github.com/docker/docker v20.10.17+incompatible
|
||||
github.com/docker/go-units v0.4.0
|
||||
github.com/docker/docker v20.10.18+incompatible
|
||||
github.com/docker/go-units v0.5.0
|
||||
github.com/emicklei/go-restful/v3 v3.9.0
|
||||
github.com/emirpasic/gods v1.18.1
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
@@ -29,23 +29,24 @@ require (
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/modood/table v0.0.0-20220527013332-8d47e76dad33
|
||||
github.com/onsi/ginkgo v1.16.5
|
||||
github.com/onsi/gomega v1.20.0
|
||||
github.com/onsi/gomega v1.20.2
|
||||
github.com/opencontainers/go-digest v1.0.1-0.20220411205349-bde1400a84be
|
||||
github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198
|
||||
github.com/opencontainers/image-spec v1.1.0-rc1
|
||||
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417
|
||||
github.com/pelletier/go-toml v1.9.5
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pkg/sftp v1.13.0
|
||||
github.com/pkg/sftp v1.13.5
|
||||
github.com/schollz/progressbar/v3 v3.8.6
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/spf13/cobra v1.5.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/wechatpay-apiv3/wechatpay-go v0.2.14
|
||||
go.uber.org/zap v1.21.0
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
|
||||
golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f
|
||||
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261
|
||||
golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0
|
||||
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
|
||||
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8
|
||||
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035
|
||||
google.golang.org/grpc v1.48.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
@@ -65,31 +66,30 @@ require (
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.1.1 // indirect
|
||||
github.com/Microsoft/go-winio v0.5.2 // indirect
|
||||
github.com/Microsoft/hcsshim v0.9.3 // indirect
|
||||
github.com/Microsoft/hcsshim v0.9.5 // indirect
|
||||
github.com/VividCortex/ewma v1.2.0 // indirect
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/blang/semver v3.5.1+incompatible // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
|
||||
github.com/containerd/cgroups v1.0.3 // indirect
|
||||
github.com/containerd/containerd v1.6.6 // indirect
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.12.0 // indirect
|
||||
github.com/containernetworking/cni v1.1.1 // indirect
|
||||
github.com/chzyer/readline v1.5.1 // indirect
|
||||
github.com/containerd/cgroups v1.0.4 // indirect
|
||||
github.com/containerd/containerd v1.6.8 // indirect
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.12.1 // indirect
|
||||
github.com/containernetworking/cni v1.1.2 // indirect
|
||||
github.com/containernetworking/plugins v1.1.1 // indirect
|
||||
github.com/containers/libtrust v0.0.0-20200511145503-9c3a6c22cd9a // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.3 // indirect
|
||||
github.com/disiqueira/gotree/v3 v3.0.2 // indirect
|
||||
github.com/docker/docker-credential-helpers v0.6.4 // indirect
|
||||
github.com/docker/docker-credential-helpers v0.7.0 // indirect
|
||||
github.com/docker/go-connections v0.4.1-0.20210727194412-58542c764a11 // indirect
|
||||
github.com/docker/go-metrics v0.0.1 // indirect
|
||||
github.com/docker/libnetwork v0.8.0-dev.2.0.20190625141545-5a177b73e316 // indirect
|
||||
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||
github.com/fsouza/go-dockerclient v1.7.11 // indirect
|
||||
github.com/fsouza/go-dockerclient v1.8.3 // indirect
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/go-errors/errors v1.4.2 // indirect
|
||||
github.com/go-logr/logr v1.2.3 // indirect
|
||||
@@ -103,8 +103,8 @@ require (
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/google/gnostic v0.6.9 // indirect
|
||||
github.com/google/go-cmp v0.5.8 // indirect
|
||||
github.com/google/go-containerregistry v0.10.0 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/go-containerregistry v0.11.0 // indirect
|
||||
github.com/google/go-intervals v0.0.2 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
@@ -114,16 +114,15 @@ require (
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/huandu/xstrings v1.3.2 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||
github.com/ishidawataru/sctp v0.0.0-20210226210310-f2269e66cdee // indirect
|
||||
github.com/jinzhu/copier v0.3.5 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.15.9 // indirect
|
||||
github.com/klauspost/pgzip v1.2.5 // indirect
|
||||
github.com/klauspost/compress v1.15.12 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6-0.20220930104621-17e8dac29df8 // indirect
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/kr/pretty v0.3.0 // indirect
|
||||
github.com/letsencrypt/boulder v0.0.0-20220331220046-b23ab962616e // indirect
|
||||
github.com/letsencrypt/boulder v0.0.0-20220723181115-27de4befb95e // indirect
|
||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
|
||||
github.com/lithammer/dedent v1.1.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
@@ -132,13 +131,13 @@ require (
|
||||
github.com/mattn/go-shellwords v1.0.12 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
||||
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible // indirect
|
||||
github.com/mistifyio/go-zfs/v3 v3.0.0 // indirect
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/moby/ipvs v1.0.2 // indirect
|
||||
github.com/moby/locker v1.0.1 // indirect
|
||||
github.com/moby/sys/mount v0.2.0 // indirect
|
||||
github.com/moby/sys/mount v0.3.3 // indirect
|
||||
github.com/moby/sys/mountinfo v0.6.2 // indirect
|
||||
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
@@ -147,10 +146,10 @@ require (
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/nxadm/tail v1.4.8 // indirect
|
||||
github.com/opencontainers/runc v1.1.3 // indirect
|
||||
github.com/opencontainers/runtime-tools v0.9.0 // indirect
|
||||
github.com/opencontainers/selinux v1.10.1 // indirect
|
||||
github.com/openshift/imagebuilder v1.2.4-0.20220502172744-009dbc6cb805 // indirect
|
||||
github.com/opencontainers/runc v1.1.4 // indirect
|
||||
github.com/opencontainers/runtime-tools v0.9.1-0.20220714195903-17b3287fafb7 // indirect
|
||||
github.com/opencontainers/selinux v1.10.2 // indirect
|
||||
github.com/openshift/imagebuilder v1.2.4-0.20220711175835-4151e43600df // indirect
|
||||
github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f // indirect
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
@@ -162,33 +161,33 @@ require (
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.8.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646 // indirect
|
||||
github.com/seccomp/libseccomp-golang v0.10.0 // indirect
|
||||
github.com/shopspring/decimal v1.2.0 // indirect
|
||||
github.com/sigstore/sigstore v1.3.1-0.20220629021053-b95fc0d626c1 // indirect
|
||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||
github.com/sigstore/sigstore v1.4.2 // indirect
|
||||
github.com/spf13/cast v1.4.1 // indirect
|
||||
github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980 // indirect
|
||||
github.com/sylabs/sif/v2 v2.7.1 // indirect
|
||||
github.com/sylabs/sif/v2 v2.8.0 // indirect
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
|
||||
github.com/tchap/go-patricia v2.3.0+incompatible // indirect
|
||||
github.com/theupdateframework/go-tuf v0.3.1 // indirect
|
||||
github.com/theupdateframework/go-tuf v0.5.1 // indirect
|
||||
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
|
||||
github.com/ulikunitz/xz v0.5.10 // indirect
|
||||
github.com/vbatts/tar-split v0.11.2 // indirect
|
||||
github.com/vbauerster/mpb/v7 v7.4.2 // indirect
|
||||
github.com/vbauerster/mpb/v7 v7.5.3 // indirect
|
||||
github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5 // indirect
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
github.com/xlab/treeprint v1.1.0 // indirect
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 // indirect
|
||||
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.8.0 // indirect
|
||||
golang.org/x/net v0.0.0-20220805013720-a33c5aa5df48 // indirect
|
||||
golang.org/x/net v0.0.0-20220909164309-bea034e7d591 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
@@ -245,10 +244,7 @@ replace (
|
||||
k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.24.3
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/containers/storage => github.com/containers/storage v1.40.2
|
||||
github.com/vishvananda/netlink => github.com/vishvananda/netlink v1.1.0
|
||||
)
|
||||
replace github.com/vishvananda/netlink => github.com/vishvananda/netlink v1.1.0
|
||||
|
||||
replace (
|
||||
github.com/labring/image-cri-shim => ./staging/src/github.com/labring/image-cri-shim
|
||||
|
||||
+700
-263
File diff suppressed because it is too large
Load Diff
@@ -145,7 +145,7 @@ func (c *Applier) reconcileCluster() error {
|
||||
|
||||
func (c *Applier) initCluster() error {
|
||||
logger.Info("Start to create a new cluster: master %s, worker %s, registry %s", c.ClusterDesired.GetMasterIPList(), c.ClusterDesired.GetNodeIPList(), c.ClusterDesired.GetRegistryIP())
|
||||
createProcessor, err := processor.NewCreateProcessor(c.ClusterFile)
|
||||
createProcessor, err := processor.NewCreateProcessor(c.ClusterDesired.Name, c.ClusterFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -213,7 +213,7 @@ func (c *Applier) Delete() error {
|
||||
}
|
||||
|
||||
func (c *Applier) deleteCluster() error {
|
||||
deleteProcessor, err := processor.NewDeleteProcessor(c.ClusterFile)
|
||||
deleteProcessor, err := processor.NewDeleteProcessor(c.ClusterDesired.Name, c.ClusterFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -23,14 +23,13 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
|
||||
"github.com/labring/sealos/pkg/bootstrap"
|
||||
"github.com/labring/sealos/pkg/buildah"
|
||||
"github.com/labring/sealos/pkg/checker"
|
||||
"github.com/labring/sealos/pkg/clusterfile"
|
||||
"github.com/labring/sealos/pkg/config"
|
||||
"github.com/labring/sealos/pkg/constants"
|
||||
"github.com/labring/sealos/pkg/filesystem"
|
||||
"github.com/labring/sealos/pkg/guest"
|
||||
"github.com/labring/sealos/pkg/image"
|
||||
"github.com/labring/sealos/pkg/image/types"
|
||||
"github.com/labring/sealos/pkg/runtime"
|
||||
v2 "github.com/labring/sealos/pkg/types/v1beta1"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
@@ -39,12 +38,10 @@ import (
|
||||
)
|
||||
|
||||
type CreateProcessor struct {
|
||||
ClusterFile clusterfile.Interface
|
||||
ImageManager types.ImageService
|
||||
ClusterManager types.ClusterService
|
||||
RegistryManager types.RegistryService
|
||||
Runtime runtime.Interface
|
||||
Guest guest.Interface
|
||||
ClusterFile clusterfile.Interface
|
||||
Buildah buildah.Interface
|
||||
Runtime runtime.Interface
|
||||
Guest guest.Interface
|
||||
}
|
||||
|
||||
func (c *CreateProcessor) Execute(cluster *v2.Cluster) error {
|
||||
@@ -91,12 +88,12 @@ func (c *CreateProcessor) Check(cluster *v2.Cluster) error {
|
||||
}
|
||||
|
||||
func (c *CreateProcessor) CheckImageType(cluster *v2.Cluster) error {
|
||||
ociList, err := c.ImageManager.Inspect(cluster.Spec.Image...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
imageTypes := sets.NewString()
|
||||
for _, oci := range ociList {
|
||||
for _, image := range cluster.Spec.Image {
|
||||
oci, err := c.Buildah.InspectImage(image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if oci.Config.Labels != nil {
|
||||
imageTypes.Insert(oci.Config.Labels[constants.ImageTypeKey])
|
||||
} else {
|
||||
@@ -111,7 +108,7 @@ func (c *CreateProcessor) CheckImageType(cluster *v2.Cluster) error {
|
||||
|
||||
func (c *CreateProcessor) PreProcess(cluster *v2.Cluster) error {
|
||||
logger.Info("Executing pipeline PreProcess in CreateProcessor.")
|
||||
err := c.RegistryManager.Pull(types.DefaultPlatform(), types.PullPolicyMissing, cluster.Spec.Image...)
|
||||
err := c.Buildah.Pull(buildah.DefaultPlatform(), buildah.PullIfMissing.String(), cluster.Spec.Image...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -119,21 +116,21 @@ func (c *CreateProcessor) PreProcess(cluster *v2.Cluster) error {
|
||||
return err
|
||||
}
|
||||
for _, img := range cluster.Spec.Image {
|
||||
clusterManifest, err := c.ClusterManager.Create(fmt.Sprintf("%s-%s", cluster.Name, rand.Generator(8)), img)
|
||||
bderInfo, err := c.Buildah.Create(rand.Generator(8), img)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mount := &v2.MountImage{
|
||||
Name: clusterManifest.Container,
|
||||
Name: bderInfo.Container,
|
||||
ImageName: img,
|
||||
MountPoint: clusterManifest.MountPoint,
|
||||
MountPoint: bderInfo.MountPoint,
|
||||
}
|
||||
if err = OCIToImageMount(mount, c.ImageManager); err != nil {
|
||||
if err = OCIToImageMount(mount, c.Buildah); err != nil {
|
||||
return err
|
||||
}
|
||||
cluster.Status.Mounts = append(cluster.Status.Mounts, *mount)
|
||||
}
|
||||
if err = SyncClusterStatus(cluster, c.ClusterManager, c.ImageManager, false); err != nil {
|
||||
if err = SyncClusterStatus(cluster, c.Buildah, false); err != nil {
|
||||
return err
|
||||
}
|
||||
runTime, err := runtime.NewDefaultRuntime(cluster, c.ClusterFile.GetKubeadmConfig())
|
||||
@@ -212,32 +209,19 @@ func (c *CreateProcessor) RunGuest(cluster *v2.Cluster) error {
|
||||
return c.Guest.Apply(cluster, cluster.Status.Mounts)
|
||||
}
|
||||
|
||||
func NewCreateProcessor(clusterFile clusterfile.Interface) (Interface, error) {
|
||||
imgSvc, err := image.NewImageService()
|
||||
func NewCreateProcessor(name string, clusterFile clusterfile.Interface) (Interface, error) {
|
||||
bder, err := buildah.New(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clusterSvc, err := image.NewClusterService()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
registrySvc, err := image.NewRegistryService()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gs, err := guest.NewGuestManager()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &CreateProcessor{
|
||||
ClusterFile: clusterFile,
|
||||
ImageManager: imgSvc,
|
||||
ClusterManager: clusterSvc,
|
||||
RegistryManager: registrySvc,
|
||||
Guest: gs,
|
||||
ClusterFile: clusterFile,
|
||||
Buildah: bder,
|
||||
Guest: gs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -26,10 +26,9 @@ import (
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/labring/sealos/pkg/buildah"
|
||||
"github.com/labring/sealos/pkg/clusterfile"
|
||||
"github.com/labring/sealos/pkg/filesystem"
|
||||
"github.com/labring/sealos/pkg/image"
|
||||
"github.com/labring/sealos/pkg/image/types"
|
||||
"github.com/labring/sealos/pkg/runtime"
|
||||
v2 "github.com/labring/sealos/pkg/types/v1beta1"
|
||||
)
|
||||
@@ -37,9 +36,8 @@ import (
|
||||
var ForceDelete bool
|
||||
|
||||
type DeleteProcessor struct {
|
||||
ClusterManager types.ClusterService
|
||||
ImageManager types.ImageService
|
||||
ClusterFile clusterfile.Interface
|
||||
Buildah buildah.Interface
|
||||
ClusterFile clusterfile.Interface
|
||||
}
|
||||
|
||||
// Execute :according to the different of desired cluster to delete cluster.
|
||||
@@ -70,7 +68,7 @@ func (d DeleteProcessor) GetPipeLine() ([]func(cluster *v2.Cluster) error, error
|
||||
}
|
||||
|
||||
func (d *DeleteProcessor) PreProcess(cluster *v2.Cluster) error {
|
||||
return SyncClusterStatus(cluster, d.ClusterManager, d.ImageManager, true)
|
||||
return SyncClusterStatus(cluster, d.Buildah, true)
|
||||
}
|
||||
|
||||
func (d *DeleteProcessor) Reset(cluster *v2.Cluster) error {
|
||||
@@ -102,7 +100,7 @@ func (d DeleteProcessor) UnMountImage(cluster *v2.Cluster) error {
|
||||
for _, mount := range cluster.Status.Mounts {
|
||||
mount := mount
|
||||
eg.Go(func() error {
|
||||
return d.ClusterManager.Delete(mount.Name)
|
||||
return d.Buildah.Delete(mount.Name)
|
||||
})
|
||||
}
|
||||
return eg.Wait()
|
||||
@@ -114,19 +112,14 @@ func (d DeleteProcessor) CleanFS(cluster *v2.Cluster) error {
|
||||
return fileutil.CleanFiles(workDir, dataDir)
|
||||
}
|
||||
|
||||
func NewDeleteProcessor(clusterFile clusterfile.Interface) (Interface, error) {
|
||||
imgSvc, err := image.NewImageService()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clusterSvc, err := image.NewClusterService()
|
||||
func NewDeleteProcessor(name string, clusterFile clusterfile.Interface) (Interface, error) {
|
||||
bder, err := buildah.New(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return DeleteProcessor{
|
||||
ClusterFile: clusterFile,
|
||||
ImageManager: imgSvc,
|
||||
ClusterManager: clusterSvc,
|
||||
Buildah: bder,
|
||||
ClusterFile: clusterFile,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -20,31 +20,26 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
|
||||
"github.com/labring/sealos/pkg/buildah"
|
||||
"github.com/labring/sealos/pkg/clusterfile"
|
||||
"github.com/labring/sealos/pkg/config"
|
||||
"github.com/labring/sealos/pkg/constants"
|
||||
"github.com/labring/sealos/pkg/filesystem"
|
||||
"github.com/labring/sealos/pkg/guest"
|
||||
v2 "github.com/labring/sealos/pkg/types/v1beta1"
|
||||
"github.com/labring/sealos/pkg/utils/confirm"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
"github.com/labring/sealos/pkg/utils/rand"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/labring/sealos/pkg/clusterfile"
|
||||
"github.com/labring/sealos/pkg/config"
|
||||
"github.com/labring/sealos/pkg/filesystem"
|
||||
"github.com/labring/sealos/pkg/guest"
|
||||
"github.com/labring/sealos/pkg/image"
|
||||
"github.com/labring/sealos/pkg/image/types"
|
||||
v2 "github.com/labring/sealos/pkg/types/v1beta1"
|
||||
)
|
||||
|
||||
var ForceOverride bool
|
||||
|
||||
type InstallProcessor struct {
|
||||
ClusterFile clusterfile.Interface
|
||||
ImageManager types.ImageService
|
||||
ClusterManager types.ClusterService
|
||||
RegistryManager types.RegistryService
|
||||
Buildah buildah.Interface
|
||||
Guest guest.Interface
|
||||
NewMounts []v2.MountImage
|
||||
NewImages []string
|
||||
@@ -106,7 +101,7 @@ func (c *InstallProcessor) SyncStatusAndCheck(cluster *v2.Cluster) error {
|
||||
return err
|
||||
}
|
||||
current := c.ClusterFile.GetCluster()
|
||||
if err = SyncClusterStatus(current, c.ClusterManager, c.ImageManager, false); err != nil {
|
||||
if err = SyncClusterStatus(current, c.Buildah, false); err != nil {
|
||||
return err
|
||||
}
|
||||
imageList := sets.NewString(current.Spec.Image...)
|
||||
@@ -119,15 +114,15 @@ func (c *InstallProcessor) SyncStatusAndCheck(cluster *v2.Cluster) error {
|
||||
}
|
||||
|
||||
func (c *InstallProcessor) PreProcess(cluster *v2.Cluster) error {
|
||||
if err := c.RegistryManager.Pull(types.DefaultPlatform(), types.PullPolicyMissing, c.NewImages...); err != nil {
|
||||
return err
|
||||
}
|
||||
ociList, err := c.ImageManager.Inspect(c.NewImages...)
|
||||
if err != nil {
|
||||
if err := c.Buildah.Pull(buildah.DefaultPlatform(), buildah.PullIfMissing.String(), c.NewImages...); err != nil {
|
||||
return err
|
||||
}
|
||||
imageTypes := sets.NewString()
|
||||
for _, oci := range ociList {
|
||||
for _, image := range c.NewImages {
|
||||
oci, err := c.Buildah.InspectImage(image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if oci.Config.Labels != nil {
|
||||
imageTypes.Insert(oci.Config.Labels[constants.ImageTypeKey])
|
||||
} else {
|
||||
@@ -142,22 +137,22 @@ func (c *InstallProcessor) PreProcess(cluster *v2.Cluster) error {
|
||||
if mount == nil {
|
||||
// create
|
||||
mount = &v2.MountImage{
|
||||
Name: fmt.Sprintf("%s-%s", cluster.Name, rand.Generator(8)),
|
||||
Name: rand.Generator(8),
|
||||
ImageName: img,
|
||||
}
|
||||
cluster.Spec.Image = append(cluster.Spec.Image, img)
|
||||
cluster.Spec.Image = merge(cluster.Spec.Image, img)
|
||||
} else if !ForceOverride {
|
||||
continue
|
||||
} else {
|
||||
logger.Debug("trying to override app %s", img)
|
||||
}
|
||||
if mount != nil {
|
||||
manifest, err := c.ClusterManager.Create(mount.Name, img)
|
||||
manifest, err := c.Buildah.Create(mount.Name, img)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mount.MountPoint = manifest.MountPoint
|
||||
if err = OCIToImageMount(mount, c.ImageManager); err != nil {
|
||||
if err = OCIToImageMount(mount, c.Buildah); err != nil {
|
||||
return err
|
||||
}
|
||||
cluster.SetMountImage(mount)
|
||||
@@ -167,6 +162,17 @@ func (c *InstallProcessor) PreProcess(cluster *v2.Cluster) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func merge(ss []string, s string) []string {
|
||||
var ret []string
|
||||
for i := range ss {
|
||||
if ss[i] != s {
|
||||
ret = append(ret, ss[i])
|
||||
}
|
||||
}
|
||||
ret = append(ret, s)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (c *InstallProcessor) PostProcess(*v2.Cluster) error {
|
||||
if len(c.NewMounts) == 0 {
|
||||
logger.Info("no apps has been changed")
|
||||
@@ -216,17 +222,7 @@ func (c *InstallProcessor) RunGuest(cluster *v2.Cluster) error {
|
||||
}
|
||||
|
||||
func NewInstallProcessor(clusterFile clusterfile.Interface, images []string) (Interface, error) {
|
||||
imgSvc, err := image.NewImageService()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clusterSvc, err := image.NewClusterService()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
registrySvc, err := image.NewRegistryService()
|
||||
bder, err := buildah.New(clusterFile.GetCluster().Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -237,11 +233,9 @@ func NewInstallProcessor(clusterFile clusterfile.Interface, images []string) (In
|
||||
}
|
||||
|
||||
return &InstallProcessor{
|
||||
ClusterFile: clusterFile,
|
||||
ImageManager: imgSvc,
|
||||
ClusterManager: clusterSvc,
|
||||
RegistryManager: registrySvc,
|
||||
Guest: gs,
|
||||
NewImages: images,
|
||||
ClusterFile: clusterFile,
|
||||
Buildah: bder,
|
||||
Guest: gs,
|
||||
NewImages: images,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -18,11 +18,12 @@ import (
|
||||
"context"
|
||||
"path"
|
||||
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/labring/sealos/pkg/buildah"
|
||||
"github.com/labring/sealos/pkg/constants"
|
||||
"github.com/labring/sealos/pkg/filesystem/registry"
|
||||
"github.com/labring/sealos/pkg/image/types"
|
||||
"github.com/labring/sealos/pkg/ssh"
|
||||
v2 "github.com/labring/sealos/pkg/types/v1beta1"
|
||||
"github.com/labring/sealos/pkg/utils/confirm"
|
||||
@@ -51,28 +52,28 @@ func SyncNewVersionConfig(cluster *v2.Cluster) {
|
||||
}
|
||||
}
|
||||
|
||||
func SyncClusterStatus(cluster *v2.Cluster, service types.ClusterService, imgService types.ImageService, reset bool) error {
|
||||
func SyncClusterStatus(cluster *v2.Cluster, bdah buildah.Interface, reset bool) error {
|
||||
if cluster.Status.Mounts == nil {
|
||||
containers, err := service.List()
|
||||
containers, err := bdah.ListContainers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cluster.Status.Mounts = make([]v2.MountImage, 0)
|
||||
for _, info := range containers {
|
||||
manifest, err := service.Inspect(info.Containername)
|
||||
for _, ctr := range containers {
|
||||
bderInfo, err := bdah.InspectContainer(ctr.ContainerName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mount := &v2.MountImage{
|
||||
MountPoint: manifest.MountPoint,
|
||||
ImageName: info.Imagename,
|
||||
Name: info.Containername,
|
||||
MountPoint: bderInfo.MountPoint,
|
||||
ImageName: ctr.ImageName,
|
||||
Name: ctr.ContainerName,
|
||||
}
|
||||
if err = OCIToImageMount(mount, imgService); err != nil {
|
||||
if err = OCIToImageMount(mount, bdah); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if reset || strings.InList(info.Imagename, cluster.Spec.Image) {
|
||||
if reset || strings.InList(ctr.ImageName, cluster.Spec.Image) {
|
||||
cluster.Status.Mounts = append(cluster.Status.Mounts, *mount)
|
||||
}
|
||||
}
|
||||
@@ -81,41 +82,44 @@ func SyncClusterStatus(cluster *v2.Cluster, service types.ClusterService, imgSer
|
||||
return nil
|
||||
}
|
||||
|
||||
func OCIToImageMount(mount *v2.MountImage, imgService types.ImageService) error {
|
||||
oci, err := imgService.Inspect(mount.ImageName)
|
||||
type imageInspector interface {
|
||||
InspectImage(string) (v1.Image, error)
|
||||
}
|
||||
|
||||
func OCIToImageMount(mount *v2.MountImage, inspector imageInspector) error {
|
||||
oci, err := inspector.InspectImage(mount.ImageName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(oci) > 0 {
|
||||
mount.Env = maps.ListToMap(oci[0].Config.Env)
|
||||
delete(mount.Env, "PATH")
|
||||
// mount.Entrypoint
|
||||
var entrypoint []string
|
||||
for _, cmd := range oci[0].Config.Entrypoint {
|
||||
if cmd == "/bin/sh" || cmd == "-c" {
|
||||
continue
|
||||
}
|
||||
entrypoint = append(entrypoint, cmd)
|
||||
}
|
||||
mount.Entrypoint = entrypoint
|
||||
|
||||
//mount.Cmd
|
||||
cmds := oci[0].Config.Cmd
|
||||
var newCMDs []string
|
||||
for _, cmd := range cmds {
|
||||
if cmd == "/bin/sh" || cmd == "-c" {
|
||||
continue
|
||||
}
|
||||
newCMDs = append(newCMDs, cmd)
|
||||
mount.Env = maps.ListToMap(oci.Config.Env)
|
||||
delete(mount.Env, "PATH")
|
||||
// mount.Entrypoint
|
||||
var entrypoint []string
|
||||
for _, cmd := range oci.Config.Entrypoint {
|
||||
if cmd == "/bin/sh" || cmd == "-c" {
|
||||
continue
|
||||
}
|
||||
mount.Cmd = newCMDs
|
||||
mount.Labels = oci[0].Config.Labels
|
||||
imageType := v2.AppImage
|
||||
if mount.Labels[constants.ImageTypeKey] != "" {
|
||||
imageType = v2.ImageType(mount.Labels[constants.ImageTypeKey])
|
||||
}
|
||||
mount.Type = imageType
|
||||
entrypoint = append(entrypoint, cmd)
|
||||
}
|
||||
mount.Entrypoint = entrypoint
|
||||
|
||||
//mount.Cmd
|
||||
cmds := oci.Config.Cmd
|
||||
var newCMDs []string
|
||||
for _, cmd := range cmds {
|
||||
if cmd == "/bin/sh" || cmd == "-c" {
|
||||
continue
|
||||
}
|
||||
newCMDs = append(newCMDs, cmd)
|
||||
}
|
||||
mount.Cmd = newCMDs
|
||||
mount.Labels = oci.Config.Labels
|
||||
imageType := v2.AppImage
|
||||
if mount.Labels[constants.ImageTypeKey] != "" {
|
||||
imageType = v2.ImageType(mount.Labels[constants.ImageTypeKey])
|
||||
}
|
||||
mount.Type = imageType
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -21,13 +21,12 @@ import (
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/labring/sealos/pkg/bootstrap"
|
||||
"github.com/labring/sealos/pkg/buildah"
|
||||
"github.com/labring/sealos/pkg/checker"
|
||||
"github.com/labring/sealos/pkg/clusterfile"
|
||||
"github.com/labring/sealos/pkg/config"
|
||||
"github.com/labring/sealos/pkg/constants"
|
||||
"github.com/labring/sealos/pkg/filesystem"
|
||||
"github.com/labring/sealos/pkg/image"
|
||||
"github.com/labring/sealos/pkg/image/types"
|
||||
"github.com/labring/sealos/pkg/runtime"
|
||||
v2 "github.com/labring/sealos/pkg/types/v1beta1"
|
||||
fileutil "github.com/labring/sealos/pkg/utils/file"
|
||||
@@ -38,8 +37,7 @@ import (
|
||||
type ScaleProcessor struct {
|
||||
ClusterFile clusterfile.Interface
|
||||
Runtime runtime.Interface
|
||||
ImageManager types.ImageService
|
||||
ClusterManager types.ClusterService
|
||||
Buildah buildah.Interface
|
||||
pullImages []string
|
||||
MastersToJoin []string
|
||||
MastersToDelete []string
|
||||
@@ -167,7 +165,7 @@ func (c *ScaleProcessor) PreProcess(cluster *v2.Cluster) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = SyncClusterStatus(cluster, c.ClusterManager, c.ImageManager, false); err != nil {
|
||||
if err = SyncClusterStatus(cluster, c.Buildah, false); err != nil {
|
||||
return err
|
||||
}
|
||||
if c.IsScaleUp {
|
||||
@@ -200,7 +198,7 @@ func (c *ScaleProcessor) PreProcessImage(cluster *v2.Cluster) error {
|
||||
}
|
||||
dirs, _ := fileutil.GetAllSubDirs(mount.MountPoint)
|
||||
if len(dirs) == 0 {
|
||||
clusterManifest, err := c.ClusterManager.Create(mount.Name, mount.ImageName)
|
||||
clusterManifest, err := c.Buildah.Create(mount.Name, mount.ImageName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -256,31 +254,18 @@ func (c *ScaleProcessor) Bootstrap(cluster *v2.Cluster) error {
|
||||
}
|
||||
|
||||
func NewScaleProcessor(clusterFile clusterfile.Interface, images v2.ImageList, masterToJoin, masterToDelete, nodeToJoin, nodeToDelete []string) (Interface, error) {
|
||||
imgSvc, err := image.NewImageService()
|
||||
bder, err := buildah.New(clusterFile.GetCluster().Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clusterSvc, err := image.NewClusterService()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var up bool
|
||||
// only scale up or scale down at a time
|
||||
if len(masterToJoin) > 0 || len(nodeToJoin) > 0 {
|
||||
up = true
|
||||
}
|
||||
|
||||
return &ScaleProcessor{
|
||||
MastersToDelete: masterToDelete,
|
||||
MastersToJoin: masterToJoin,
|
||||
NodesToDelete: nodeToDelete,
|
||||
NodesToJoin: nodeToJoin,
|
||||
ClusterFile: clusterFile,
|
||||
ImageManager: imgSvc,
|
||||
ClusterManager: clusterSvc,
|
||||
Buildah: bder,
|
||||
pullImages: images,
|
||||
IsScaleUp: up,
|
||||
IsScaleUp: len(masterToJoin) > 0 || len(nodeToJoin) > 0,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -35,6 +35,12 @@ type Applier interface {
|
||||
Undo(Context, string) error
|
||||
}
|
||||
|
||||
var (
|
||||
defaultCheckers []Applier
|
||||
defaultInitializers []Applier
|
||||
defaultAddons []Applier
|
||||
)
|
||||
|
||||
type registryApplier struct {
|
||||
}
|
||||
|
||||
@@ -107,3 +113,7 @@ func newHtpasswdMaker(root string) *htpasswdMaker {
|
||||
func (*registryApplier) Undo(ctx Context, host string) error {
|
||||
return ctx.GetExecer().CmdAsync(host, ctx.GetShellWrapper()(host, ctx.GetBash().CleanRegistryBash()))
|
||||
}
|
||||
|
||||
func init() {
|
||||
defaultAddons = append(defaultAddons, ®istryApplier{})
|
||||
}
|
||||
|
||||
@@ -58,9 +58,9 @@ func New(cluster *v2.Cluster) Interface {
|
||||
addons: make([]Applier, 0),
|
||||
}
|
||||
// register builtin appliers
|
||||
_ = bs.RegisterApplier(Preflight, &defaultChecker{})
|
||||
_ = bs.RegisterApplier(Init, &defaultInitializer{})
|
||||
_ = bs.RegisterApplier(Addon, ®istryApplier{})
|
||||
_ = bs.RegisterApplier(Preflight, defaultCheckers...)
|
||||
_ = bs.RegisterApplier(Init, defaultInitializers...)
|
||||
_ = bs.RegisterApplier(Addon, defaultAddons...)
|
||||
return bs
|
||||
}
|
||||
|
||||
@@ -179,3 +179,22 @@ func (initializer *defaultInitializer) Apply(ctx Context, host string) error {
|
||||
func (initializer *defaultInitializer) Undo(_ Context, _ string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
defaultCheckers = append(defaultCheckers, &defaultChecker{})
|
||||
defaultInitializers = append(defaultInitializers, &defaultInitializer{})
|
||||
}
|
||||
|
||||
func RegisterApplier(phase Phase, appliers ...Applier) error {
|
||||
switch phase {
|
||||
case Preflight:
|
||||
defaultCheckers = append(defaultCheckers, appliers...)
|
||||
case Init:
|
||||
defaultInitializers = append(defaultInitializers, appliers...)
|
||||
case Addon:
|
||||
defaultAddons = append(defaultAddons, appliers...)
|
||||
default:
|
||||
return fmt.Errorf("unknown phase %s", phase)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
// Copyright © 2022 buildah.
|
||||
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 buildah
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/containers/buildah/imagebuildah"
|
||||
buildahcli "github.com/containers/buildah/pkg/cli"
|
||||
"github.com/containers/buildah/pkg/parse"
|
||||
"github.com/containers/buildah/util"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
)
|
||||
|
||||
func newBuildCommand() *cobra.Command {
|
||||
buildDescription := `
|
||||
Builds an OCI image using instructions in one or more Containerfiles.
|
||||
|
||||
If no arguments are specified, Buildah will use the current working directory
|
||||
as the build context and look for a Containerfile. The build fails if no
|
||||
Containerfile nor Dockerfile is present.`
|
||||
|
||||
layerFlagsResults := buildahcli.LayerResults{}
|
||||
buildFlagResults := buildahcli.BudResults{}
|
||||
fromAndBudResults := buildahcli.FromAndBudResults{}
|
||||
userNSResults := buildahcli.UserNSResults{}
|
||||
namespaceResults := buildahcli.NameSpaceResults{}
|
||||
sopts := saveOptions{}
|
||||
|
||||
buildCommand := &cobra.Command{
|
||||
Use: "build [CONTEXT]",
|
||||
Aliases: []string{"build-using-dockerfile", "bud"},
|
||||
Short: "Build an image using instructions in a Containerfile or Kubefile",
|
||||
Long: buildDescription,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
br := buildahcli.BuildOptions{
|
||||
LayerResults: &layerFlagsResults,
|
||||
BudResults: &buildFlagResults,
|
||||
UserNSResults: &userNSResults,
|
||||
FromAndBudResults: &fromAndBudResults,
|
||||
NameSpaceResults: &namespaceResults,
|
||||
}
|
||||
return buildCmd(cmd, args, sopts, br)
|
||||
},
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
Example: fmt.Sprintf(`%[1]s build
|
||||
%[1]s bud -f Kubefile.simple .
|
||||
%[1]s bud -f Kubefile.simple -f Kubefile.notsosimple .`, rootCmd.Name()),
|
||||
}
|
||||
buildCommand.SetUsageTemplate(UsageTemplate())
|
||||
|
||||
flags := buildCommand.Flags()
|
||||
flags.SetInterspersed(false)
|
||||
|
||||
// build is a all common flags
|
||||
buildFlags := buildahcli.GetBudFlags(&buildFlagResults)
|
||||
buildFlags.StringVar(&buildFlagResults.Runtime, "runtime", util.Runtime(), "`path` to an alternate runtime. Use BUILDAH_RUNTIME environment variable to override.")
|
||||
|
||||
layerFlags := buildahcli.GetLayerFlags(&layerFlagsResults)
|
||||
fromAndBudFlags, err := buildahcli.GetFromAndBudFlags(&fromAndBudResults, &userNSResults, &namespaceResults)
|
||||
bailOnError(err, "failed to setup From and Build flags")
|
||||
|
||||
// set as default, otherwise parse.PlatformsFromOptions will get empty list
|
||||
err = fromAndBudFlags.Set("platform", parse.DefaultPlatform())
|
||||
bailOnError(err, "failed to set default platform flag")
|
||||
|
||||
sopts.RegisterFlags(flags)
|
||||
flags.AddFlagSet(&buildFlags)
|
||||
flags.AddFlagSet(&layerFlags)
|
||||
flags.AddFlagSet(&fromAndBudFlags)
|
||||
flags.SetNormalizeFunc(buildahcli.AliasFlags)
|
||||
|
||||
return buildCommand
|
||||
}
|
||||
|
||||
func buildCmd(c *cobra.Command, inputArgs []string, sopts saveOptions, iopts buildahcli.BuildOptions) error {
|
||||
if flagChanged(c, "logfile") {
|
||||
logfile, err := os.OpenFile(iopts.Logfile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iopts.Logwriter = logfile
|
||||
defer iopts.Logwriter.Close()
|
||||
}
|
||||
|
||||
options, containerfiles, removeAll, err := buildahcli.GenBuildOptions(c, inputArgs, iopts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
for _, f := range removeAll {
|
||||
os.RemoveAll(f)
|
||||
}
|
||||
}()
|
||||
|
||||
platforms, err := parsePlatforms(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = runSaveImages(options.ContextDirectory, platforms, &sopts); err != nil {
|
||||
return err
|
||||
}
|
||||
if globalFlagResults.DefaultMountsFile != "" {
|
||||
options.DefaultMountsFilePath = globalFlagResults.DefaultMountsFile
|
||||
}
|
||||
|
||||
store, err := getStore(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
id, ref, err := imagebuildah.BuildDockerfiles(getContext(), store, options, containerfiles...)
|
||||
if err == nil && options.Manifest != "" {
|
||||
logger.Debug("manifest list id = %q, ref = %q", id, ref.String())
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
// Copyright © 2022 buildah.
|
||||
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 buildah
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
|
||||
"github.com/containers/buildah/pkg/parse"
|
||||
"github.com/containers/common/pkg/config"
|
||||
"github.com/containers/storage"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
)
|
||||
|
||||
type globalFlags struct {
|
||||
LogLevel string
|
||||
Root string
|
||||
RunRoot string
|
||||
StorageDriver string
|
||||
RegistriesConf string
|
||||
RegistriesConfDir string
|
||||
DefaultMountsFile string
|
||||
StorageOpts []string
|
||||
UserNSUID []string
|
||||
UserNSGID []string
|
||||
CPUProfile string
|
||||
cpuProfileFile *os.File
|
||||
MemoryProfile string
|
||||
UserShortNameAliasConfPath string
|
||||
CgroupManager string
|
||||
}
|
||||
|
||||
func (opts *globalFlags) HiddenFlags() []string {
|
||||
// hidden most of buildah flags
|
||||
return []string{
|
||||
"registries-conf",
|
||||
"registries-conf-dir",
|
||||
"short-name-alias-conf",
|
||||
"cgroup-manager",
|
||||
"userns-uid-map",
|
||||
"userns-gid-map",
|
||||
logLevel,
|
||||
"cpu-profile",
|
||||
"default-mounts-file",
|
||||
"memory-profile",
|
||||
}
|
||||
}
|
||||
|
||||
const logLevel = "log-level"
|
||||
|
||||
func RegisterGlobalFlags(fs *pflag.FlagSet) error {
|
||||
var (
|
||||
defaultStoreDriverOptions []string
|
||||
)
|
||||
storageOptions, err := storage.DefaultStoreOptions(false, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(storageOptions.GraphDriverOptions) > 0 {
|
||||
optionSlice := storageOptions.GraphDriverOptions[:]
|
||||
defaultStoreDriverOptions = optionSlice
|
||||
}
|
||||
|
||||
containerConfig, err := config.Default()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
containerConfig.CheckCgroupsAndAdjustConfig()
|
||||
// TODO Need to allow for environment variable
|
||||
fs.StringVar(&globalFlagResults.RegistriesConf, "registries-conf", "", "path to registries.conf file (not usually used)")
|
||||
fs.StringVar(&globalFlagResults.RegistriesConfDir, "registries-conf-dir", "", "path to registries.conf.d directory (not usually used)")
|
||||
fs.StringVar(&globalFlagResults.UserShortNameAliasConfPath, "short-name-alias-conf", "", "path to short name alias cache file (not usually used)")
|
||||
fs.StringVar(&globalFlagResults.Root, "root", storageOptions.GraphRoot, "storage root dir")
|
||||
fs.StringVar(&globalFlagResults.RunRoot, "runroot", storageOptions.RunRoot, "storage state dir")
|
||||
fs.StringVar(&globalFlagResults.CgroupManager, "cgroup-manager", containerConfig.Engine.CgroupManager, "cgroup manager")
|
||||
fs.StringVar(&globalFlagResults.StorageDriver, "storage-driver", storageOptions.GraphDriverName, "storage-driver")
|
||||
fs.StringSliceVar(&globalFlagResults.StorageOpts, "storage-opt", defaultStoreDriverOptions, "storage driver option")
|
||||
fs.StringSliceVar(&globalFlagResults.UserNSUID, "userns-uid-map", []string{}, "default `ctrID:hostID:length` UID mapping to use")
|
||||
fs.StringSliceVar(&globalFlagResults.UserNSGID, "userns-gid-map", []string{}, "default `ctrID:hostID:length` GID mapping to use")
|
||||
fs.StringVar(&globalFlagResults.DefaultMountsFile, "default-mounts-file", "", "path to default mounts file")
|
||||
fs.StringVar(&globalFlagResults.LogLevel, logLevel, "warn", `The log level to be used. Either "trace", "debug", "info", "warn", "error", "fatal", or "panic".`)
|
||||
fs.StringVar(&globalFlagResults.CPUProfile, "cpu-profile", "", "`file` to write CPU profile")
|
||||
fs.StringVar(&globalFlagResults.MemoryProfile, "memory-profile", "", "`file` to write memory profile")
|
||||
return markFlagsHidden(fs, globalFlagResults.HiddenFlags()...)
|
||||
}
|
||||
|
||||
var (
|
||||
globalFlagResults globalFlags
|
||||
rootCmd *cobra.Command
|
||||
postRunHooks []func() error
|
||||
)
|
||||
|
||||
func markFlagsHidden(fs *pflag.FlagSet, names ...string) error {
|
||||
for _, name := range names {
|
||||
if err := fs.MarkHidden(name); err != nil {
|
||||
return fmt.Errorf("unable to mark %s flag as hidden: %v", name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func subCommands() []*cobra.Command {
|
||||
return []*cobra.Command{
|
||||
newBuildCommand(),
|
||||
newContainersCommand(),
|
||||
newFromCommand(),
|
||||
newImagesCommand(),
|
||||
newInspectCommand(),
|
||||
newLoadCommand(),
|
||||
newLoginCommand(),
|
||||
newLogoutCommand(),
|
||||
newManifestCommand(),
|
||||
newMountCommand(),
|
||||
newPullCommand(),
|
||||
newPushCommand(),
|
||||
newRMCommand(),
|
||||
newRMICommand(),
|
||||
newSaveCommand(),
|
||||
newTagCommand(),
|
||||
newUmountCommand(),
|
||||
}
|
||||
}
|
||||
|
||||
func RegisterRootCommand(cmd *cobra.Command) {
|
||||
os.Setenv("TMPDIR", parse.GetTempDir())
|
||||
rootCmd = cmd
|
||||
cmd.SilenceUsage = true
|
||||
err := RegisterGlobalFlags(cmd.PersistentFlags())
|
||||
bailOnError(err, "failed to register global flags")
|
||||
wrapPrePersistentRun(cmd)
|
||||
wrapPostPersistentRun(cmd)
|
||||
cmd.AddCommand(subCommands()...)
|
||||
}
|
||||
|
||||
func RegisterPostRun(fn func() error) {
|
||||
if rootCmd == nil {
|
||||
logger.Fatal("Must not register post run function before RegisterRootCommand")
|
||||
}
|
||||
postRunHooks = append(postRunHooks, fn)
|
||||
}
|
||||
|
||||
func wrapPrePersistentRun(cmd *cobra.Command) {
|
||||
switch cmd.Use {
|
||||
case "", "version":
|
||||
return
|
||||
}
|
||||
switch {
|
||||
case cmd.PersistentPreRun != nil:
|
||||
run := cmd.PersistentPreRun
|
||||
cmd.PersistentPreRun = func(cmd *cobra.Command, args []string) {
|
||||
if err := TrySetupWithDefaults(defaultSetters...); err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
run(cmd, args)
|
||||
}
|
||||
case cmd.PersistentPreRunE != nil:
|
||||
runE := cmd.PersistentPreRunE
|
||||
cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
|
||||
if err := TrySetupWithDefaults(defaultSetters...); err != nil {
|
||||
return err
|
||||
}
|
||||
return runE(cmd, args)
|
||||
}
|
||||
default:
|
||||
cmd.PersistentPreRun = func(cmd *cobra.Command, args []string) {
|
||||
if err := TrySetupWithDefaults(defaultSetters...); err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func wrapPostPersistentRun(cmd *cobra.Command) {
|
||||
// shutdown store if necessary
|
||||
switch {
|
||||
case cmd.PersistentPostRun != nil:
|
||||
run := cmd.PersistentPostRun
|
||||
cmd.PersistentPostRun = func(cmd *cobra.Command, args []string) {
|
||||
if err := after(cmd); err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
run(cmd, args)
|
||||
}
|
||||
case cmd.PersistentPostRunE != nil:
|
||||
runE := cmd.PersistentPostRunE
|
||||
cmd.PersistentPostRunE = func(cmd *cobra.Command, args []string) error {
|
||||
if err := after(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
return runE(cmd, args)
|
||||
}
|
||||
default:
|
||||
cmd.PersistentPostRun = func(cmd *cobra.Command, args []string) {
|
||||
if err := after(cmd); err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func shutdownStore(cmd *cobra.Command) error {
|
||||
if needToShutdownStore {
|
||||
store, err := getStore(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Debug("shutting down the store")
|
||||
needToShutdownStore = false
|
||||
if _, err = store.Shutdown(false); err != nil {
|
||||
if errors.Is(err, storage.ErrLayerUsedByContainer) {
|
||||
logger.Info("failed to shutdown storage: %q", err)
|
||||
} else {
|
||||
logger.Warn("failed to shutdown storage: %q", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func after(cmd *cobra.Command) error {
|
||||
if err := shutdownStore(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if globalFlagResults.CPUProfile != "" {
|
||||
pprof.StopCPUProfile()
|
||||
globalFlagResults.cpuProfileFile.Close()
|
||||
}
|
||||
if globalFlagResults.MemoryProfile != "" {
|
||||
memoryProfileFile, err := os.Create(globalFlagResults.MemoryProfile)
|
||||
if err != nil {
|
||||
logger.Fatal("could not create memory profile %s: %v", globalFlagResults.MemoryProfile, err)
|
||||
}
|
||||
defer memoryProfileFile.Close()
|
||||
runtime.GC()
|
||||
if err := pprof.Lookup("heap").WriteTo(memoryProfileFile, 1); err != nil {
|
||||
logger.Fatal("could not write memory profile %s: %v", globalFlagResults.MemoryProfile, err)
|
||||
}
|
||||
}
|
||||
for i := range postRunHooks {
|
||||
if err := postRunHooks[i](); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
original := defaultSetters
|
||||
defaultSetters = []Setter{maybeSetupLogrusLogger, maybeSetupProfiler}
|
||||
defaultSetters = append(defaultSetters, original...)
|
||||
}
|
||||
|
||||
func maybeSetupLogrusLogger() (err error) {
|
||||
logrusLvl, err := logrus.ParseLevel(globalFlagResults.LogLevel)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse log level: %w", err)
|
||||
}
|
||||
if logrusLvl <= logrus.DebugLevel {
|
||||
logrus.SetLevel(logrusLvl)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func maybeSetupProfiler() (err error) {
|
||||
if globalFlagResults.CPUProfile != "" {
|
||||
cpuProfileFile, err := os.Create(globalFlagResults.CPUProfile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not create CPU profile %s: %v", globalFlagResults.CPUProfile, err)
|
||||
}
|
||||
globalFlagResults.cpuProfileFile = cpuProfileFile
|
||||
if err = pprof.StartCPUProfile(globalFlagResults.cpuProfileFile); err != nil {
|
||||
return fmt.Errorf("error starting CPU profiling: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
// Copyright © 2022 buildah.
|
||||
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 buildah
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/containers/buildah"
|
||||
"github.com/containers/common/pkg/umask"
|
||||
is "github.com/containers/image/v5/storage"
|
||||
"github.com/containers/image/v5/types"
|
||||
"github.com/containers/storage"
|
||||
"github.com/containers/storage/pkg/unshare"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
)
|
||||
|
||||
var (
|
||||
// configuration, including customizations made in containers.conf
|
||||
needToShutdownStore = false
|
||||
)
|
||||
|
||||
var DefaultPlatform = platforms.DefaultSpec
|
||||
|
||||
func flagChanged(c *cobra.Command, name string) bool {
|
||||
if fs := c.Flag(name); fs != nil && fs.Changed {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func bailOnError(err error, format string, a ...interface{}) { // nolint: golint,goprintffuncname
|
||||
if err != nil {
|
||||
if format != "" {
|
||||
logger.Error("%s: %v", fmt.Sprintf(format, a...), err)
|
||||
} else {
|
||||
logger.Error("%v", err)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func getStore(c *cobra.Command) (storage.Store, error) {
|
||||
if err := setXDGRuntimeDir(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options, err := storage.DefaultStoreOptions(unshare.GetRootlessUID() > 0, unshare.GetRootlessUID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if flagChanged(c, "root") || flagChanged(c, "runroot") {
|
||||
options.GraphRoot = globalFlagResults.Root
|
||||
options.RunRoot = globalFlagResults.RunRoot
|
||||
}
|
||||
if flagChanged(c, "storage-driver") {
|
||||
options.GraphDriverName = globalFlagResults.StorageDriver
|
||||
// If any options setup in config, these should be dropped if user overrode the driver
|
||||
options.GraphDriverOptions = []string{}
|
||||
}
|
||||
if flagChanged(c, "storage-opt") {
|
||||
if len(globalFlagResults.StorageOpts) > 0 {
|
||||
options.GraphDriverOptions = globalFlagResults.StorageOpts
|
||||
}
|
||||
}
|
||||
|
||||
// Do not allow to mount a graphdriver that is not vfs if we are creating the userns as part
|
||||
// of the mount command.
|
||||
// Differently, allow the mount if we are already in a userns, as the mount point will still
|
||||
// be accessible once "buildah mount" exits.
|
||||
if os.Geteuid() != 0 && options.GraphDriverName != "vfs" {
|
||||
return nil, fmt.Errorf("cannot mount using driver %s in rootless mode. You need to run it in a `buildah unshare` session", options.GraphDriverName)
|
||||
}
|
||||
|
||||
if len(globalFlagResults.UserNSUID) > 0 {
|
||||
uopts := globalFlagResults.UserNSUID
|
||||
gopts := globalFlagResults.UserNSGID
|
||||
|
||||
if len(gopts) == 0 {
|
||||
gopts = uopts
|
||||
}
|
||||
|
||||
uidmap, gidmap, err := unshare.ParseIDMappings(uopts, gopts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options.UIDMap = uidmap
|
||||
options.GIDMap = gidmap
|
||||
} else {
|
||||
if len(globalFlagResults.UserNSGID) > 0 {
|
||||
return nil, errors.New("option --userns-gid-map can not be used without --userns-uid-map")
|
||||
}
|
||||
}
|
||||
|
||||
// If a subcommand has the flags, check if they are set; if so, override the global values
|
||||
if flagChanged(c, "userns-uid-map") {
|
||||
uopts, _ := c.Flags().GetStringSlice("userns-uid-map")
|
||||
gopts, _ := c.Flags().GetStringSlice("userns-gid-map")
|
||||
if len(gopts) == 0 {
|
||||
gopts = uopts
|
||||
}
|
||||
uidmap, gidmap, err := unshare.ParseIDMappings(uopts, gopts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options.UIDMap = uidmap
|
||||
options.GIDMap = gidmap
|
||||
} else {
|
||||
if flagChanged(c, "userns-gid-map") {
|
||||
return nil, errors.New("option --userns-gid-map can not be used without --userns-uid-map")
|
||||
}
|
||||
}
|
||||
umask.Check()
|
||||
|
||||
store, err := storage.GetStore(options)
|
||||
if store != nil {
|
||||
is.Transport.SetStore(store)
|
||||
}
|
||||
// Do we really need to shutdown store before exit?
|
||||
// needToShutdownStore = true
|
||||
return store, err
|
||||
}
|
||||
|
||||
// setXDGRuntimeDir sets XDG_RUNTIME_DIR when if it is unset under rootless
|
||||
func setXDGRuntimeDir() error {
|
||||
if IsRootless() && os.Getenv("XDG_RUNTIME_DIR") == "" {
|
||||
runtimeDir, err := storage.GetRootlessRuntimeDir(unshare.GetRootlessUID())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Setenv("XDG_RUNTIME_DIR", runtimeDir); err != nil {
|
||||
return errors.New("could not set XDG_RUNTIME_DIR")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func openBuilder(ctx context.Context, store storage.Store, name string) (builder *buildah.Builder, err error) {
|
||||
if name != "" {
|
||||
builder, err = buildah.OpenBuilder(store, name)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
options := buildah.ImportOptions{
|
||||
Container: name,
|
||||
}
|
||||
builder, err = buildah.ImportBuilder(ctx, store, options)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if builder == nil {
|
||||
return nil, errors.New("finding build container")
|
||||
}
|
||||
return builder, nil
|
||||
}
|
||||
|
||||
func openBuilders(store storage.Store) (builders []*buildah.Builder, err error) {
|
||||
return buildah.OpenAllBuilders(store)
|
||||
}
|
||||
|
||||
func openImage(ctx context.Context, sc *types.SystemContext, store storage.Store, name string) (builder *buildah.Builder, err error) {
|
||||
options := buildah.ImportFromImageOptions{
|
||||
Image: name,
|
||||
SystemContext: sc,
|
||||
}
|
||||
builder, err = buildah.ImportBuilderFromImage(ctx, store, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if builder == nil {
|
||||
return nil, errors.New("mocking up build configuration")
|
||||
}
|
||||
return builder, nil
|
||||
}
|
||||
|
||||
// getContext returns a context.TODO
|
||||
func getContext() context.Context {
|
||||
return context.TODO()
|
||||
}
|
||||
|
||||
func defaultFormat() string {
|
||||
format := os.Getenv("BUILDAH_FORMAT")
|
||||
if format != "" {
|
||||
return format
|
||||
}
|
||||
return buildah.OCI
|
||||
}
|
||||
|
||||
// Tail returns a string slice after the first element unless there are
|
||||
// not enough elements, then it returns an empty slice. This is to replace
|
||||
// the urfavecli Tail method for args
|
||||
func Tail(a []string) []string {
|
||||
if len(a) >= 2 {
|
||||
return a[1:]
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// UsageTemplate returns the usage template for podman commands
|
||||
// This blocks the displaying of the global options. The main podman
|
||||
// command should not use this.
|
||||
func UsageTemplate() string {
|
||||
return `Usage:{{if .Runnable}}
|
||||
{{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
|
||||
{{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
|
||||
|
||||
Aliases:
|
||||
{{.NameAndAliases}}{{end}}{{if .HasExample}}
|
||||
|
||||
Examples:
|
||||
{{.Example}}{{end}}{{if .HasAvailableSubCommands}}
|
||||
|
||||
Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
|
||||
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
|
||||
|
||||
Flags:
|
||||
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
|
||||
{{end}}
|
||||
`
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright © 2021 sealos.
|
||||
// Copyright © 2022 sealos.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -12,27 +12,26 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cmd
|
||||
package buildah
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/labring/sealos/pkg/image"
|
||||
"github.com/containers/buildah/define"
|
||||
)
|
||||
|
||||
func NewPruneCmd() *cobra.Command {
|
||||
var pruneCmd = &cobra.Command{
|
||||
Use: "prune",
|
||||
Short: "prune image",
|
||||
Example: `sealos prune`,
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
registrySvc, err := image.NewImageService()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return registrySvc.Prune()
|
||||
},
|
||||
}
|
||||
return pruneCmd
|
||||
}
|
||||
const (
|
||||
OCIArchive string = "oci-archive"
|
||||
DockerArchive string = "docker-archive"
|
||||
)
|
||||
|
||||
var DefaultTransport = DockerArchive
|
||||
|
||||
const (
|
||||
DisableAutoRootless = "DISABLE_AUTO_ROOTLESS"
|
||||
)
|
||||
|
||||
const (
|
||||
PullIfMissing = define.PullIfMissing
|
||||
PullAlways = define.PullAlways
|
||||
PullIfNewer = define.PullIfNewer
|
||||
PullNever = define.PullNever
|
||||
)
|
||||
@@ -0,0 +1,352 @@
|
||||
// Copyright © 2022 buildah.
|
||||
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 buildah
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/buildah"
|
||||
"github.com/containers/buildah/define"
|
||||
"github.com/containers/buildah/pkg/formats"
|
||||
"github.com/containers/buildah/util"
|
||||
"github.com/containers/storage"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
var containersHeader = map[string]string{
|
||||
"ContainerName": "CONTAINER NAME",
|
||||
"ContainerID": "CONTAINER ID",
|
||||
"Builder": "BUILDER",
|
||||
"ImageID": "IMAGE ID",
|
||||
"ImageName": "IMAGE NAME",
|
||||
}
|
||||
|
||||
type JSONContainer struct {
|
||||
ID string `json:"id"`
|
||||
Builder bool `json:"builder"`
|
||||
ImageID string `json:"imageid"`
|
||||
ImageName string `json:"imagename"`
|
||||
ContainerName string `json:"containername"`
|
||||
}
|
||||
|
||||
type containerOutputParams struct {
|
||||
ContainerID string
|
||||
Builder string
|
||||
ImageID string
|
||||
ImageName string
|
||||
ContainerName string
|
||||
}
|
||||
|
||||
type containerOptions struct {
|
||||
all bool
|
||||
format string
|
||||
json bool
|
||||
noHeading bool
|
||||
noTruncate bool
|
||||
quiet bool
|
||||
}
|
||||
|
||||
type containerFilterParams struct {
|
||||
id string
|
||||
name string
|
||||
ancestor string
|
||||
}
|
||||
|
||||
type containersResults struct {
|
||||
all bool
|
||||
filter string
|
||||
format string
|
||||
json bool
|
||||
noheading bool
|
||||
notruncate bool
|
||||
quiet bool
|
||||
}
|
||||
|
||||
func newDefaultContainerResults() *containersResults {
|
||||
return &containersResults{}
|
||||
}
|
||||
|
||||
func (opts *containersResults) RegisterFlags(fs *pflag.FlagSet) {
|
||||
fs.BoolVarP(&opts.all, "all", "a", opts.all, "also list non-buildah containers")
|
||||
fs.StringVarP(&opts.filter, "filter", "f", opts.filter, "filter output based on conditions provided")
|
||||
fs.StringVar(&opts.format, "format", opts.format, "pretty-print containers using a Go template")
|
||||
fs.BoolVar(&opts.json, "json", opts.json, "output in JSON format")
|
||||
fs.BoolVarP(&opts.noheading, "noheading", "n", opts.noheading, "do not print column headings")
|
||||
fs.BoolVar(&opts.notruncate, "notruncate", opts.notruncate, "do not truncate output")
|
||||
fs.BoolVarP(&opts.quiet, "quiet", "q", opts.quiet, "display only container IDs")
|
||||
}
|
||||
|
||||
func newContainersCommand() *cobra.Command {
|
||||
var (
|
||||
containersDescription = "\n Lists containers which appear to be " + define.Package + " working containers, their\n names and IDs, and the names and IDs of the images from which they were\n initialized."
|
||||
opts = newDefaultContainerResults()
|
||||
)
|
||||
containersCommand := &cobra.Command{
|
||||
Use: "containers",
|
||||
Hidden: true,
|
||||
Aliases: []string{"list", "ls", "ps"},
|
||||
Short: "List working containers and their base images",
|
||||
Long: containersDescription,
|
||||
Args: cobra.ExactArgs(0),
|
||||
//Flags: sortFlags(containersFlags),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return containersCmd(cmd, args, opts)
|
||||
},
|
||||
Example: fmt.Sprintf(`%[1]s containers
|
||||
%[1]s containers --format "{{.ContainerID}} {{.ContainerName}}"
|
||||
%[1]s containers -q --noheading --notruncate`, rootCmd.Name()),
|
||||
}
|
||||
containersCommand.SetUsageTemplate(UsageTemplate())
|
||||
|
||||
opts.RegisterFlags(containersCommand.Flags())
|
||||
return containersCommand
|
||||
}
|
||||
|
||||
func containersCmd(c *cobra.Command, args []string, iopts *containersResults) error {
|
||||
if len(args) > 0 {
|
||||
return fmt.Errorf("'%s containers' does not accept arguments", rootCmd.Name())
|
||||
}
|
||||
store, err := getStore(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if flagChanged(c, "quiet") && flagChanged(c, "format") {
|
||||
return errors.New("quiet and format are mutually exclusive")
|
||||
}
|
||||
|
||||
opts := containerOptions{
|
||||
all: iopts.all,
|
||||
format: iopts.format,
|
||||
json: iopts.json,
|
||||
noHeading: iopts.noheading,
|
||||
noTruncate: iopts.notruncate,
|
||||
quiet: iopts.quiet,
|
||||
}
|
||||
|
||||
var params *containerFilterParams
|
||||
if flagChanged(c, "filter") {
|
||||
params, err = parseCtrFilter(iopts.filter)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing filter: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if !opts.noHeading && !opts.quiet && opts.format == "" && !opts.json {
|
||||
containerOutputHeader(!opts.noTruncate)
|
||||
}
|
||||
|
||||
return outputContainers(store, opts, params)
|
||||
}
|
||||
|
||||
func readContainers(store storage.Store, opts containerOptions, params *containerFilterParams) ([]containerOutputParams, []JSONContainer, error) {
|
||||
seenImages := make(map[string]string)
|
||||
imageNameForID := func(id string) string {
|
||||
if id == "" {
|
||||
return buildah.BaseImageFakeName
|
||||
}
|
||||
imageName, ok := seenImages[id]
|
||||
if ok {
|
||||
return imageName
|
||||
}
|
||||
img, err2 := store.Image(id)
|
||||
if err2 == nil && len(img.Names) > 0 {
|
||||
seenImages[id] = img.Names[0]
|
||||
}
|
||||
return seenImages[id]
|
||||
}
|
||||
|
||||
builders, err := openBuilders(store)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("reading build containers: %w", err)
|
||||
}
|
||||
var (
|
||||
containerOutput []containerOutputParams
|
||||
JSONContainers []JSONContainer
|
||||
)
|
||||
if !opts.all {
|
||||
// only output containers created by buildah
|
||||
for _, builder := range builders {
|
||||
image := imageNameForID(builder.FromImageID)
|
||||
if !matchesCtrFilter(builder.ContainerID, builder.Container, builder.FromImageID, image, params) {
|
||||
continue
|
||||
}
|
||||
if opts.json {
|
||||
JSONContainers = append(JSONContainers, JSONContainer{ID: builder.ContainerID,
|
||||
Builder: true,
|
||||
ImageID: builder.FromImageID,
|
||||
ImageName: image,
|
||||
ContainerName: builder.Container})
|
||||
continue
|
||||
}
|
||||
output := containerOutputParams{
|
||||
ContainerID: builder.ContainerID,
|
||||
Builder: " *",
|
||||
ImageID: builder.FromImageID,
|
||||
ImageName: image,
|
||||
ContainerName: builder.Container,
|
||||
}
|
||||
containerOutput = append(containerOutput, output)
|
||||
}
|
||||
} else {
|
||||
// output all containers currently in storage
|
||||
builderMap := make(map[string]struct{})
|
||||
for _, builder := range builders {
|
||||
builderMap[builder.ContainerID] = struct{}{}
|
||||
}
|
||||
containers, err2 := store.Containers()
|
||||
if err2 != nil {
|
||||
return nil, nil, fmt.Errorf("reading list of all containers: %w", err2)
|
||||
}
|
||||
for _, container := range containers {
|
||||
name := ""
|
||||
if len(container.Names) > 0 {
|
||||
name = container.Names[0]
|
||||
}
|
||||
_, ours := builderMap[container.ID]
|
||||
builder := ""
|
||||
if ours {
|
||||
builder = " *"
|
||||
}
|
||||
if !matchesCtrFilter(container.ID, name, container.ImageID, imageNameForID(container.ImageID), params) {
|
||||
continue
|
||||
}
|
||||
if opts.json {
|
||||
JSONContainers = append(JSONContainers, JSONContainer{ID: container.ID,
|
||||
Builder: ours,
|
||||
ImageID: container.ImageID,
|
||||
ImageName: imageNameForID(container.ImageID),
|
||||
ContainerName: name})
|
||||
continue
|
||||
}
|
||||
output := containerOutputParams{
|
||||
ContainerID: container.ID,
|
||||
Builder: builder,
|
||||
ImageID: container.ImageID,
|
||||
ImageName: imageNameForID(container.ImageID),
|
||||
ContainerName: name,
|
||||
}
|
||||
containerOutput = append(containerOutput, output)
|
||||
}
|
||||
}
|
||||
return containerOutput, JSONContainers, nil
|
||||
}
|
||||
|
||||
func outputContainers(store storage.Store, opts containerOptions, params *containerFilterParams) error {
|
||||
containerOutput, JSONContainers, err := readContainers(store, opts, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if opts.json {
|
||||
data, err := json.MarshalIndent(JSONContainers, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%s\n", data)
|
||||
return nil
|
||||
}
|
||||
|
||||
if opts.format != "" {
|
||||
out := formats.StdoutTemplateArray{Output: containersToGeneric(containerOutput), Template: opts.format, Fields: containersHeader}
|
||||
return formats.Writer(out).Out()
|
||||
}
|
||||
|
||||
for _, ctr := range containerOutput {
|
||||
if opts.quiet {
|
||||
fmt.Printf("%-64s\n", ctr.ContainerID)
|
||||
continue
|
||||
}
|
||||
containerOutputUsingFormatString(!opts.noTruncate, ctr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func containersToGeneric(templParams []containerOutputParams) (genericParams []interface{}) {
|
||||
if len(templParams) > 0 {
|
||||
for _, v := range templParams {
|
||||
genericParams = append(genericParams, interface{}(v))
|
||||
}
|
||||
}
|
||||
return genericParams
|
||||
}
|
||||
|
||||
func containerOutputUsingFormatString(truncate bool, params containerOutputParams) {
|
||||
if truncate {
|
||||
fmt.Printf("%-12.12s %-8s %-12.12s %-32s %s\n", params.ContainerID, params.Builder, params.ImageID, util.TruncateString(params.ImageName, 32), params.ContainerName)
|
||||
} else {
|
||||
fmt.Printf("%-64s %-8s %-64s %-32s %s\n", params.ContainerID, params.Builder, params.ImageID, params.ImageName, params.ContainerName)
|
||||
}
|
||||
}
|
||||
|
||||
func containerOutputHeader(truncate bool) {
|
||||
if truncate {
|
||||
fmt.Printf("%-12s %-8s %-12s %-32s %s\n", "CONTAINER ID", "BUILDER", "IMAGE ID", "IMAGE NAME", "CONTAINER NAME")
|
||||
} else {
|
||||
fmt.Printf("%-64s %-8s %-64s %-32s %s\n", "CONTAINER ID", "BUILDER", "IMAGE ID", "IMAGE NAME", "CONTAINER NAME")
|
||||
}
|
||||
}
|
||||
|
||||
func parseCtrFilter(filter string) (*containerFilterParams, error) {
|
||||
params := new(containerFilterParams)
|
||||
filters := strings.Split(filter, ",")
|
||||
for _, param := range filters {
|
||||
pair := strings.SplitN(param, "=", 2)
|
||||
if len(pair) != 2 {
|
||||
return nil, fmt.Errorf("incorrect filter value %q, should be of form filter=value", param)
|
||||
}
|
||||
switch strings.TrimSpace(pair[0]) {
|
||||
case "id":
|
||||
params.id = pair[1]
|
||||
case "name":
|
||||
params.name = pair[1]
|
||||
case "ancestor":
|
||||
params.ancestor = pair[1]
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid filter %q", pair[0])
|
||||
}
|
||||
}
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func matchesCtrName(ctrName, argName string) bool {
|
||||
return strings.Contains(ctrName, argName)
|
||||
}
|
||||
|
||||
func matchesAncestor(imgName, imgID, argName string) bool {
|
||||
if matchesID(imgID, argName) {
|
||||
return true
|
||||
}
|
||||
return matchesReference(imgName, argName)
|
||||
}
|
||||
|
||||
func matchesCtrFilter(ctrID, ctrName, imgID, imgName string, params *containerFilterParams) bool {
|
||||
if params == nil {
|
||||
return true
|
||||
}
|
||||
if params.id != "" && !matchesID(ctrID, params.id) {
|
||||
return false
|
||||
}
|
||||
if params.name != "" && !matchesCtrName(ctrName, params.name) {
|
||||
return false
|
||||
}
|
||||
if params.ancestor != "" && !matchesAncestor(imgName, imgID, params.ancestor) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -12,8 +12,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package image
|
||||
package buildah
|
||||
|
||||
func (d *Service) Remove(force bool, images ...string) error {
|
||||
return d.Rmi(false, force, images)
|
||||
}
|
||||
// Most of codes are COPIED from github.com/containers/buildah
|
||||
@@ -0,0 +1,392 @@
|
||||
// Copyright © 2022 buildah.
|
||||
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 buildah
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/containers/buildah"
|
||||
"github.com/containers/buildah/define"
|
||||
buildahcli "github.com/containers/buildah/pkg/cli"
|
||||
"github.com/containers/buildah/pkg/parse"
|
||||
"github.com/containers/common/pkg/auth"
|
||||
"github.com/containers/common/pkg/config"
|
||||
"github.com/containers/image/v5/types"
|
||||
"github.com/containers/storage"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/labring/sealos/pkg/buildah/internal/util"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
)
|
||||
|
||||
type fromReply struct {
|
||||
authfile string
|
||||
certDir string
|
||||
cidfile string
|
||||
creds string
|
||||
format string
|
||||
name string
|
||||
pull string
|
||||
pullAlways bool
|
||||
pullNever bool
|
||||
quiet bool
|
||||
signaturePolicy string
|
||||
tlsVerify bool
|
||||
*buildahcli.FromAndBudResults
|
||||
*buildahcli.UserNSResults
|
||||
*buildahcli.NameSpaceResults
|
||||
}
|
||||
|
||||
func newDefaultFromReply() *fromReply {
|
||||
defaultContainerConfig, err := config.Default()
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
return &fromReply{
|
||||
authfile: auth.GetDefaultAuthFile(),
|
||||
format: defaultFormat(),
|
||||
pull: "true",
|
||||
tlsVerify: true,
|
||||
FromAndBudResults: &buildahcli.FromAndBudResults{
|
||||
Devices: defaultContainerConfig.Containers.Devices,
|
||||
DNSSearch: defaultContainerConfig.Containers.DNSSearches,
|
||||
DNSServers: defaultContainerConfig.Containers.DNSServers,
|
||||
DNSOptions: defaultContainerConfig.Containers.DNSOptions,
|
||||
HTTPProxy: true,
|
||||
Isolation: buildahcli.DefaultIsolation(),
|
||||
Retry: buildahcli.MaxPullPushRetries,
|
||||
RetryDelay: buildahcli.PullPushRetryDelay.String(),
|
||||
ShmSize: defaultContainerConfig.Containers.ShmSize,
|
||||
Ulimit: defaultContainerConfig.Containers.DefaultUlimits,
|
||||
Volumes: defaultContainerConfig.Containers.Volumes,
|
||||
},
|
||||
UserNSResults: &buildahcli.UserNSResults{},
|
||||
NameSpaceResults: &buildahcli.NameSpaceResults{},
|
||||
}
|
||||
}
|
||||
|
||||
func (opts *fromReply) RegisterFlags(fs *pflag.FlagSet) {
|
||||
fs.SetInterspersed(false)
|
||||
fs.StringVar(&opts.authfile, "authfile", opts.authfile, "path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
|
||||
fs.StringVar(&opts.certDir, "cert-dir", opts.certDir, "use certificates at the specified path to access the registry")
|
||||
fs.StringVar(&opts.cidfile, "cidfile", opts.cidfile, "write the container ID to the file")
|
||||
fs.StringVar(&opts.creds, "creds", opts.creds, "use `[username[:password]]` for accessing the registry")
|
||||
fs.StringVarP(&opts.format, "format", "f", opts.format, "`format` of the image manifest and metadata")
|
||||
fs.StringVar(&opts.name, "name", opts.name, "`name` for the working container")
|
||||
fs.StringVar(&opts.pull, "pull", opts.pull, "pull the image from the registry if newer or not present in store, if false, only pull the image if not present, if always, pull the image even if the named image is present in store, if never, only use the image present in store if available")
|
||||
fs.Lookup("pull").NoOptDefVal = "true" //allow `--pull ` to be set to `true` as expected.
|
||||
|
||||
fs.BoolVar(&opts.pullAlways, "pull-always", opts.pullAlways, "pull the image even if the named image is present in store")
|
||||
fs.BoolVar(&opts.pullNever, "pull-never", opts.pullNever, "do not pull the image, use the image present in store if available")
|
||||
fs.BoolVarP(&opts.quiet, "quiet", "q", opts.quiet, "don't output progress information when pulling images")
|
||||
fs.StringVar(&opts.signaturePolicy, "signature-policy", opts.signaturePolicy, "`pathname` of signature policy file (not usually used)")
|
||||
fs.StringVar(&suffix, "suffix", "", "suffix to add to intermediate containers")
|
||||
fs.BoolVar(&opts.tlsVerify, "tls-verify", opts.tlsVerify, "require HTTPS and verify certificates when accessing the registry. TLS verification cannot be used when talking to an insecure registry.")
|
||||
_ = markFlagsHidden(fs, "pull-always", "pull-never", "suffix", "signature-policy")
|
||||
// Add in the common flags
|
||||
fromAndBudFlags, err := buildahcli.GetFromAndBudFlags(opts.FromAndBudResults, opts.UserNSResults, opts.NameSpaceResults)
|
||||
bailOnError(err, "failed to setup From and Bud flags")
|
||||
|
||||
fs.AddFlagSet(&fromAndBudFlags)
|
||||
fs.SetNormalizeFunc(buildahcli.AliasFlags)
|
||||
}
|
||||
|
||||
var suffix string
|
||||
|
||||
func newFromCommand() *cobra.Command {
|
||||
var (
|
||||
fromDescription = "\n Creates a new working container, either from scratch or using a specified\n image as a starting point."
|
||||
opts = newDefaultFromReply()
|
||||
)
|
||||
|
||||
fromCommand := &cobra.Command{
|
||||
Use: "from",
|
||||
Hidden: true,
|
||||
Short: "Create a working container based on an image",
|
||||
Long: fromDescription,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return fromCmd(cmd, args, opts)
|
||||
},
|
||||
Example: fmt.Sprintf(`%[1]s from --pull imagename
|
||||
%[1]s from docker-daemon:imagename:imagetag
|
||||
%[1]s from --name "myimagename" myregistry/myrepository/imagename:imagetag`, rootCmd.Name()),
|
||||
}
|
||||
fromCommand.SetUsageTemplate(UsageTemplate())
|
||||
opts.RegisterFlags(fromCommand.Flags())
|
||||
|
||||
return fromCommand
|
||||
}
|
||||
|
||||
func onBuild(builder *buildah.Builder, quiet bool) error {
|
||||
ctr := 0
|
||||
for _, onBuildSpec := range builder.OnBuild() {
|
||||
ctr = ctr + 1
|
||||
commands := strings.Split(onBuildSpec, " ")
|
||||
command := strings.ToUpper(commands[0])
|
||||
args := commands[1:]
|
||||
if !quiet {
|
||||
fmt.Fprintf(os.Stderr, "STEP %d: %s\n", ctr, onBuildSpec)
|
||||
}
|
||||
switch command {
|
||||
case "ADD":
|
||||
case "COPY":
|
||||
dest := ""
|
||||
size := len(args)
|
||||
if size > 1 {
|
||||
dest = args[size-1]
|
||||
args = args[:size-1]
|
||||
}
|
||||
if err := builder.Add(dest, command == "ADD", buildah.AddAndCopyOptions{}, args...); err != nil {
|
||||
return err
|
||||
}
|
||||
case "ANNOTATION":
|
||||
annotation := strings.SplitN(args[0], "=", 2)
|
||||
if len(annotation) > 1 {
|
||||
builder.SetAnnotation(annotation[0], annotation[1])
|
||||
} else {
|
||||
builder.UnsetAnnotation(annotation[0])
|
||||
}
|
||||
case "CMD":
|
||||
builder.SetCmd(args)
|
||||
case "ENV":
|
||||
env := strings.SplitN(args[0], "=", 2)
|
||||
if len(env) > 1 {
|
||||
builder.SetEnv(env[0], env[1])
|
||||
} else {
|
||||
builder.UnsetEnv(env[0])
|
||||
}
|
||||
case "ENTRYPOINT":
|
||||
builder.SetEntrypoint(args)
|
||||
case "EXPOSE":
|
||||
builder.SetPort(strings.Join(args, " "))
|
||||
case "HOSTNAME":
|
||||
builder.SetHostname(strings.Join(args, " "))
|
||||
case "LABEL":
|
||||
label := strings.SplitN(args[0], "=", 2)
|
||||
if len(label) > 1 {
|
||||
builder.SetLabel(label[0], label[1])
|
||||
} else {
|
||||
builder.UnsetLabel(label[0])
|
||||
}
|
||||
case "MAINTAINER":
|
||||
builder.SetMaintainer(strings.Join(args, " "))
|
||||
case "ONBUILD":
|
||||
builder.SetOnBuild(strings.Join(args, " "))
|
||||
case "RUN":
|
||||
var stdout io.Writer
|
||||
if quiet {
|
||||
stdout = io.Discard
|
||||
}
|
||||
if err := builder.Run(args, buildah.RunOptions{Stdout: stdout}); err != nil {
|
||||
return err
|
||||
}
|
||||
case "SHELL":
|
||||
builder.SetShell(args)
|
||||
case "STOPSIGNAL":
|
||||
builder.SetStopSignal(strings.Join(args, " "))
|
||||
case "USER":
|
||||
builder.SetUser(strings.Join(args, " "))
|
||||
case "VOLUME":
|
||||
builder.AddVolume(strings.Join(args, " "))
|
||||
case "WORKINGDIR":
|
||||
builder.SetWorkDir(strings.Join(args, " "))
|
||||
default:
|
||||
logger.Error("unknown OnBuild command %q; ignored", onBuildSpec)
|
||||
}
|
||||
}
|
||||
builder.ClearOnBuild()
|
||||
return nil
|
||||
}
|
||||
|
||||
func fromCmd(c *cobra.Command, args []string, iopts *fromReply) error {
|
||||
if len(args) == 0 {
|
||||
return errors.New("an image name (or \"scratch\") must be specified")
|
||||
}
|
||||
if err := buildahcli.VerifyFlagsArgsOrder(args); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(args) > 1 {
|
||||
return errors.New("too many arguments specified")
|
||||
}
|
||||
|
||||
if err := auth.CheckAuthFile(iopts.authfile); err != nil {
|
||||
return err
|
||||
}
|
||||
systemContext, err := parse.SystemContextFromOptions(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("building system context: %w", err)
|
||||
}
|
||||
platforms, err := parse.PlatformsFromOptions(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(platforms) > 1 {
|
||||
logger.Warn("ignoring platforms other than %+v: %+v", platforms[0], platforms[1:])
|
||||
}
|
||||
|
||||
pullFlagsCount := 0
|
||||
if c.Flag("pull").Changed {
|
||||
pullFlagsCount++
|
||||
}
|
||||
if c.Flag("pull-always").Changed {
|
||||
pullFlagsCount++
|
||||
}
|
||||
if c.Flag("pull-never").Changed {
|
||||
pullFlagsCount++
|
||||
}
|
||||
|
||||
if pullFlagsCount > 1 {
|
||||
return errors.New("can only set one of 'pull' or 'pull-always' or 'pull-never'")
|
||||
}
|
||||
|
||||
store, err := getStore(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
builder, err := doFrom(c, args[0], iopts, store, systemContext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if iopts.cidfile != "" {
|
||||
filePath := iopts.cidfile
|
||||
if err := os.WriteFile(filePath, []byte(builder.ContainerID), 0644); err != nil {
|
||||
return fmt.Errorf("failed to write container ID file %q: %w", filePath, err)
|
||||
}
|
||||
}
|
||||
fmt.Printf("%s\n", builder.Container)
|
||||
return nil
|
||||
}
|
||||
|
||||
func doFrom(c *cobra.Command, image string, iopts *fromReply,
|
||||
store storage.Store,
|
||||
systemContext *types.SystemContext,
|
||||
) (*buildah.Builder, error) {
|
||||
defaultContainerConfig, err := config.Default()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Allow for --pull, --pull=true, --pull=false, --pull=never, --pull=always
|
||||
// --pull-always and --pull-never. The --pull-never and --pull-always options
|
||||
// will not be documented.
|
||||
pullPolicy := define.PullIfMissing
|
||||
if strings.EqualFold(strings.TrimSpace(iopts.pull), "true") {
|
||||
pullPolicy = define.PullIfNewer
|
||||
}
|
||||
if iopts.pullAlways || strings.EqualFold(strings.TrimSpace(iopts.pull), "always") {
|
||||
pullPolicy = define.PullAlways
|
||||
}
|
||||
if iopts.pullNever || strings.EqualFold(strings.TrimSpace(iopts.pull), "never") {
|
||||
pullPolicy = define.PullNever
|
||||
}
|
||||
logger.Debug("Pull Policy for pull [%v]", pullPolicy)
|
||||
|
||||
commonOpts, err := parse.CommonBuildOptions(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
isolation, err := parse.IsolationOption(iopts.Isolation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
namespaceOptions, networkPolicy, err := parse.NamespaceOptions(c)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing namespace-related options: %w", err)
|
||||
}
|
||||
usernsOption, idmappingOptions, err := parse.IDMappingOptions(c, isolation)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing ID mapping options: %w", err)
|
||||
}
|
||||
namespaceOptions.AddOrReplace(usernsOption...)
|
||||
|
||||
format, err := util.GetFormat(iopts.format)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
devices := define.ContainerDevices{}
|
||||
for _, device := range append(defaultContainerConfig.Containers.Devices, iopts.Devices...) {
|
||||
dev, err := parse.DeviceFromPath(device)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
devices = append(devices, dev...)
|
||||
}
|
||||
|
||||
capabilities, err := defaultContainerConfig.Capabilities("", iopts.CapAdd, iopts.CapDrop)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
commonOpts.Ulimit = append(defaultContainerConfig.Containers.DefaultUlimits, commonOpts.Ulimit...)
|
||||
|
||||
decConfig, err := util.DecryptConfig(iopts.DecryptionKeys)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to obtain decrypt config: %w", err)
|
||||
}
|
||||
|
||||
var pullPushRetryDelay time.Duration
|
||||
pullPushRetryDelay, err = time.ParseDuration(iopts.RetryDelay)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse value provided %q as --retry-delay: %w", iopts.RetryDelay, err)
|
||||
}
|
||||
|
||||
options := buildah.BuilderOptions{
|
||||
FromImage: image,
|
||||
Container: iopts.name,
|
||||
ContainerSuffix: suffix,
|
||||
PullPolicy: pullPolicy,
|
||||
SignaturePolicyPath: iopts.signaturePolicy,
|
||||
SystemContext: systemContext,
|
||||
DefaultMountsFilePath: globalFlagResults.DefaultMountsFile,
|
||||
Isolation: isolation,
|
||||
NamespaceOptions: namespaceOptions,
|
||||
ConfigureNetwork: networkPolicy,
|
||||
CNIPluginPath: iopts.CNIPlugInPath,
|
||||
CNIConfigDir: iopts.CNIConfigDir,
|
||||
IDMappingOptions: idmappingOptions,
|
||||
Capabilities: capabilities,
|
||||
CommonBuildOpts: commonOpts,
|
||||
Format: format,
|
||||
BlobDirectory: iopts.BlobCache,
|
||||
Devices: devices,
|
||||
MaxPullRetries: iopts.Retry,
|
||||
PullRetryDelay: pullPushRetryDelay,
|
||||
OciDecryptConfig: decConfig,
|
||||
}
|
||||
|
||||
if !iopts.quiet {
|
||||
options.ReportWriter = os.Stderr
|
||||
}
|
||||
|
||||
builder, err := buildah.NewBuilder(getContext(), store, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := onBuild(builder, iopts.quiet); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := builder.Save(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return builder, nil
|
||||
}
|
||||
@@ -0,0 +1,377 @@
|
||||
// Copyright © 2022 buildah.
|
||||
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 buildah
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
buildahcli "github.com/containers/buildah/pkg/cli"
|
||||
"github.com/containers/buildah/pkg/formats"
|
||||
"github.com/containers/buildah/pkg/parse"
|
||||
"github.com/containers/common/libimage"
|
||||
"github.com/containers/image/v5/types"
|
||||
"github.com/containers/storage"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
const none = "<none>"
|
||||
|
||||
type jsonImage struct {
|
||||
ID string `json:"id"`
|
||||
Names []string `json:"names"`
|
||||
Digest string `json:"digest"`
|
||||
CreatedAt string `json:"createdat"`
|
||||
Size string `json:"size"`
|
||||
Created int64 `json:"created"`
|
||||
CreatedAtRaw time.Time `json:"createdatraw"`
|
||||
ReadOnly bool `json:"readonly"`
|
||||
History []string `json:"history"`
|
||||
}
|
||||
|
||||
type imageOutputParams struct {
|
||||
Tag string
|
||||
ID string
|
||||
Name string
|
||||
Digest string
|
||||
Created int64
|
||||
CreatedAt string
|
||||
Size string
|
||||
CreatedAtRaw time.Time
|
||||
ReadOnly bool
|
||||
History string
|
||||
}
|
||||
|
||||
type imageOptions struct {
|
||||
all bool
|
||||
digests bool
|
||||
format string
|
||||
json bool
|
||||
noHeading bool
|
||||
truncate bool
|
||||
quiet bool
|
||||
readOnly bool
|
||||
history bool
|
||||
}
|
||||
|
||||
func newDefaultImageResults() *imageResults {
|
||||
return &imageResults{
|
||||
imageOptions: imageOptions{},
|
||||
}
|
||||
}
|
||||
|
||||
func (opts *imageResults) RegisterFlags(fs *pflag.FlagSet) {
|
||||
fs.SetInterspersed(false)
|
||||
fs.BoolVarP(&opts.all, "all", "a", opts.all, "show all images, including intermediate images from a build")
|
||||
fs.BoolVar(&opts.digests, "digests", opts.digests, "show digests")
|
||||
fs.StringSliceVarP(&opts.filter, "filter", "f", opts.filter, "filter output based on conditions provided")
|
||||
fs.StringVar(&opts.format, "format", opts.format, "pretty-print images using a Go template")
|
||||
fs.BoolVar(&opts.json, "json", opts.json, "output in JSON format")
|
||||
fs.BoolVarP(&opts.noHeading, "noheading", "n", opts.noHeading, "do not print column headings")
|
||||
// TODO needs alias here -- to `notruncate`
|
||||
fs.BoolVar(&opts.truncate, "no-trunc", opts.truncate, "do not truncate output")
|
||||
fs.BoolVarP(&opts.quiet, "quiet", "q", opts.quiet, "display only image IDs")
|
||||
fs.BoolVarP(&opts.history, "history", "", opts.history, "display the image name history")
|
||||
}
|
||||
|
||||
type imageResults struct {
|
||||
imageOptions
|
||||
filter []string
|
||||
}
|
||||
|
||||
var imagesHeader = map[string]string{
|
||||
"Name": "REPOSITORY",
|
||||
"Tag": "TAG",
|
||||
"ID": "IMAGE ID",
|
||||
"CreatedAt": "CREATED",
|
||||
"Size": "SIZE",
|
||||
"ReadOnly": "R/O",
|
||||
"History": "HISTORY",
|
||||
}
|
||||
|
||||
func newImagesCommand() *cobra.Command {
|
||||
var (
|
||||
opts = newDefaultImageResults()
|
||||
imagesDescription = "\n Lists locally stored images."
|
||||
)
|
||||
imagesCommand := &cobra.Command{
|
||||
Use: "images",
|
||||
Short: "List images in local storage",
|
||||
Long: imagesDescription,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return imagesCmd(cmd, args, opts)
|
||||
},
|
||||
Example: fmt.Sprintf(`%[1]s images --all
|
||||
%[1]s images [imageName]
|
||||
%[1]s images --format '{{.ID}} {{.Name}} {{.Size}} {{.CreatedAtRaw}}'`, rootCmd.Name()),
|
||||
}
|
||||
imagesCommand.SetUsageTemplate(UsageTemplate())
|
||||
|
||||
opts.RegisterFlags(imagesCommand.Flags())
|
||||
return imagesCommand
|
||||
}
|
||||
|
||||
func imagesCmd(c *cobra.Command, args []string, iopts *imageResults) error {
|
||||
if len(args) > 0 {
|
||||
if iopts.all {
|
||||
return errors.New("when using the --all switch, you may not pass any images names or IDs")
|
||||
}
|
||||
|
||||
if err := buildahcli.VerifyFlagsArgsOrder(args); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(args) > 1 {
|
||||
return fmt.Errorf("'%s images' requires at most 1 argument", rootCmd.Name())
|
||||
}
|
||||
}
|
||||
if iopts.quiet && iopts.format != "" {
|
||||
return errors.New("quiet and format are mutually exclusive")
|
||||
}
|
||||
|
||||
store, err := getStore(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
systemContext, err := parse.SystemContextFromOptions(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("building system context: %w", err)
|
||||
}
|
||||
|
||||
images, err := readImages(store, systemContext, args, iopts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
opts := imageOptions{
|
||||
all: iopts.all,
|
||||
digests: iopts.digests,
|
||||
format: iopts.format,
|
||||
json: iopts.json,
|
||||
noHeading: iopts.noHeading,
|
||||
truncate: !iopts.truncate,
|
||||
quiet: iopts.quiet,
|
||||
history: iopts.history,
|
||||
}
|
||||
|
||||
if opts.json {
|
||||
return formatImagesJSON(images, opts)
|
||||
}
|
||||
|
||||
return formatImages(images, opts)
|
||||
}
|
||||
|
||||
func readImages(store storage.Store, systemContext *types.SystemContext, names []string, iopts *imageResults) ([]*libimage.Image, error) {
|
||||
runtime, err := libimage.RuntimeFromStore(store, &libimage.RuntimeOptions{SystemContext: systemContext})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
options := &libimage.ListImagesOptions{}
|
||||
if len(iopts.filter) > 0 {
|
||||
options.Filters = iopts.filter
|
||||
}
|
||||
if !iopts.all {
|
||||
options.Filters = append(options.Filters, "intermediate=false")
|
||||
}
|
||||
|
||||
return runtime.ListImages(ctx, names, options)
|
||||
}
|
||||
|
||||
func outputHeader(opts imageOptions) string {
|
||||
if opts.format != "" {
|
||||
return strings.Replace(opts.format, `\t`, "\t", -1)
|
||||
}
|
||||
if opts.quiet {
|
||||
return formats.IDString
|
||||
}
|
||||
format := "table {{.Name}}\t{{.Tag}}\t"
|
||||
if opts.noHeading {
|
||||
format = "{{.Name}}\t{{.Tag}}\t"
|
||||
}
|
||||
|
||||
if opts.digests {
|
||||
format += "{{.Digest}}\t"
|
||||
}
|
||||
format += "{{.ID}}\t{{.CreatedAt}}\t{{.Size}}"
|
||||
if opts.readOnly {
|
||||
format += "\t{{.ReadOnly}}"
|
||||
}
|
||||
if opts.history {
|
||||
format += "\t{{.History}}"
|
||||
}
|
||||
return format
|
||||
}
|
||||
|
||||
func formatImagesJSON(images []*libimage.Image, opts imageOptions) error {
|
||||
jsonImages := []jsonImage{}
|
||||
for _, image := range images {
|
||||
// Copy the base data over to the output param.
|
||||
size, err := image.Size()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
created := image.Created()
|
||||
jsonImages = append(jsonImages,
|
||||
jsonImage{
|
||||
CreatedAtRaw: created,
|
||||
Created: created.Unix(),
|
||||
CreatedAt: units.HumanDuration(time.Since(created)) + " ago",
|
||||
Digest: image.Digest().String(),
|
||||
ID: truncateID(image.ID(), opts.truncate),
|
||||
Names: image.Names(),
|
||||
ReadOnly: image.IsReadOnly(),
|
||||
Size: formattedSize(size),
|
||||
})
|
||||
}
|
||||
|
||||
data, err := json.MarshalIndent(jsonImages, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%s\n", data)
|
||||
return nil
|
||||
}
|
||||
|
||||
type imagesSorted []imageOutputParams
|
||||
|
||||
func (a imagesSorted) Less(i, j int) bool {
|
||||
return a[i].CreatedAtRaw.After(a[j].CreatedAtRaw)
|
||||
}
|
||||
|
||||
func (a imagesSorted) Len() int {
|
||||
return len(a)
|
||||
}
|
||||
|
||||
func (a imagesSorted) Swap(i, j int) {
|
||||
a[i], a[j] = a[j], a[i]
|
||||
}
|
||||
|
||||
func formatImages(images []*libimage.Image, opts imageOptions) error {
|
||||
var outputData imagesSorted
|
||||
|
||||
for _, image := range images {
|
||||
var outputParam imageOutputParams
|
||||
size, err := image.Size()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
created := image.Created()
|
||||
outputParam.CreatedAtRaw = created
|
||||
outputParam.Created = created.Unix()
|
||||
outputParam.CreatedAt = units.HumanDuration(time.Since(created)) + " ago"
|
||||
outputParam.Digest = image.Digest().String()
|
||||
outputParam.ID = truncateID(image.ID(), opts.truncate)
|
||||
outputParam.Size = formattedSize(size)
|
||||
outputParam.ReadOnly = image.IsReadOnly()
|
||||
|
||||
repoTags, err := image.NamedRepoTags()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nameTagPairs, err := libimage.ToNameTagPairs(repoTags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, pair := range nameTagPairs {
|
||||
newParam := outputParam
|
||||
newParam.Name = pair.Name
|
||||
newParam.Tag = pair.Tag
|
||||
newParam.History = formatHistory(image.NamesHistory(), pair.Name, pair.Tag)
|
||||
outputData = append(outputData, newParam)
|
||||
// `images -q` should a given ID only once.
|
||||
if opts.quiet {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(outputData)
|
||||
out := formats.StdoutTemplateArray{Output: imagesToGeneric(outputData), Template: outputHeader(opts), Fields: imagesHeader}
|
||||
return formats.Writer(out).Out()
|
||||
}
|
||||
|
||||
func formatHistory(history []string, name, tag string) string {
|
||||
if len(history) == 0 {
|
||||
return none
|
||||
}
|
||||
// Skip the first history entry if already existing as name
|
||||
if fmt.Sprintf("%s:%s", name, tag) == history[0] {
|
||||
if len(history) == 1 {
|
||||
return none
|
||||
}
|
||||
return strings.Join(history[1:], ", ")
|
||||
}
|
||||
return strings.Join(history, ", ")
|
||||
}
|
||||
|
||||
func truncateID(id string, truncate bool) string {
|
||||
if !truncate {
|
||||
return "sha256:" + id
|
||||
}
|
||||
idTruncLength := 12
|
||||
if len(id) > idTruncLength {
|
||||
return id[:idTruncLength]
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
func imagesToGeneric(templParams []imageOutputParams) (genericParams []interface{}) {
|
||||
if len(templParams) > 0 {
|
||||
for _, v := range templParams {
|
||||
genericParams = append(genericParams, interface{}(v))
|
||||
}
|
||||
}
|
||||
return genericParams
|
||||
}
|
||||
|
||||
func formattedSize(size int64) string {
|
||||
suffixes := [5]string{"B", "KB", "MB", "GB", "TB"}
|
||||
|
||||
count := 0
|
||||
formattedSize := float64(size)
|
||||
for formattedSize >= 1000 && count < 4 {
|
||||
formattedSize /= 1000
|
||||
count++
|
||||
}
|
||||
return fmt.Sprintf("%.3g %s", formattedSize, suffixes[count])
|
||||
}
|
||||
|
||||
func matchesID(imageID, argID string) bool {
|
||||
return strings.HasPrefix(imageID, argID)
|
||||
}
|
||||
|
||||
func matchesReference(name, argName string) bool {
|
||||
if argName == "" {
|
||||
return true
|
||||
}
|
||||
splitName := strings.Split(name, ":")
|
||||
// If the arg contains a tag, we handle it differently than if it does not
|
||||
if strings.Contains(argName, ":") {
|
||||
splitArg := strings.Split(argName, ":")
|
||||
return strings.HasSuffix(splitName[0], splitArg[0]) && (splitName[1] == splitArg[1])
|
||||
}
|
||||
return strings.HasSuffix(splitName[0], argName)
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright © 2022 sealos.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package buildah
|
||||
|
||||
import (
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/buildah/pkg/parse"
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/labring/sealos/pkg/buildimage"
|
||||
"github.com/labring/sealos/pkg/constants"
|
||||
"github.com/labring/sealos/pkg/registry"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
)
|
||||
|
||||
type saveOptions struct {
|
||||
maxPullProcs int
|
||||
enabled bool
|
||||
}
|
||||
|
||||
func (opts *saveOptions) RegisterFlags(fs *pflag.FlagSet) {
|
||||
fs.IntVar(&opts.maxPullProcs, "max-pull-procs", 5, "maximum number of goroutines for pulling")
|
||||
fs.BoolVar(&opts.enabled, "save-image", true, "save images parsed to local")
|
||||
}
|
||||
|
||||
func runSaveImages(contextDir string, platforms []v1.Platform, opts *saveOptions) error {
|
||||
if !opts.enabled {
|
||||
logger.Warn("save-image is disabled, skip pulling images")
|
||||
return nil
|
||||
}
|
||||
images, err := buildimage.List(contextDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(images) == 0 {
|
||||
return nil
|
||||
}
|
||||
auths, err := registry.GetAuthInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
is := registry.NewImageSaver(getContext(), opts.maxPullProcs, auths)
|
||||
for _, pf := range platforms {
|
||||
logger.Debug("pull images %v for platform %s", images, strings.Join([]string{pf.OS, pf.Architecture}, "/"))
|
||||
images, err = is.SaveImages(images, path.Join(contextDir, constants.RegistryDirName), pf)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to save images")
|
||||
}
|
||||
logger.Info("saving images %s", strings.Join(images, ", "))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parsePlatforms(c *cobra.Command) ([]v1.Platform, error) {
|
||||
platforms, err := parse.PlatformsFromOptions(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var ret []v1.Platform
|
||||
for _, pf := range platforms {
|
||||
ret = append(ret, v1.Platform{Architecture: pf.Arch, OS: pf.OS, Variant: pf.Variant})
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
// Copyright © 2022 buildah.
|
||||
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 buildah
|
||||
|
||||
// nosemgrep: go.lang.security.audit.xss.import-text-template.import-text-template
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"text/template"
|
||||
|
||||
"github.com/containers/buildah"
|
||||
buildahcli "github.com/containers/buildah/pkg/cli"
|
||||
"github.com/containers/buildah/pkg/parse"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
const (
|
||||
inspectTypeApp = "app"
|
||||
inspectTypeContainer = "container"
|
||||
inspectTypeImage = "image"
|
||||
inspectTypeManifest = "manifest"
|
||||
)
|
||||
|
||||
type inspectResults struct {
|
||||
format string
|
||||
inspectType string
|
||||
}
|
||||
|
||||
func newDefaultInspectResults() *inspectResults {
|
||||
return &inspectResults{
|
||||
inspectType: inspectTypeApp,
|
||||
}
|
||||
}
|
||||
|
||||
func (opts *inspectResults) RegisterFlags(fs *pflag.FlagSet) {
|
||||
fs.SetInterspersed(false)
|
||||
fs.StringVarP(&opts.format, "format", "f", "", "use `format` as a Go template to format the output")
|
||||
fs.StringVarP(&opts.inspectType, "type", "t", inspectTypeContainer, "look at the item of the specified `type` (container or image) and name")
|
||||
}
|
||||
|
||||
func newInspectCommand() *cobra.Command {
|
||||
var (
|
||||
opts = newDefaultInspectResults()
|
||||
inspectDescription = "\n Inspects a build container's or built image's configuration."
|
||||
)
|
||||
|
||||
inspectCommand := &cobra.Command{
|
||||
Use: "inspect",
|
||||
Short: "Inspect the configuration of a container or image",
|
||||
Long: inspectDescription,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return inspectCmd(cmd, args, opts)
|
||||
},
|
||||
Example: fmt.Sprintf(`%[1]s inspect containerID
|
||||
%[1]s inspect --type image imageID
|
||||
%[1]s inspect --format '{{.OCIv1.Config.Env}}' alpine`, rootCmd.Name()),
|
||||
}
|
||||
inspectCommand.SetUsageTemplate(UsageTemplate())
|
||||
opts.RegisterFlags(inspectCommand.Flags())
|
||||
return inspectCommand
|
||||
}
|
||||
|
||||
func inspectCmd(c *cobra.Command, args []string, iopts *inspectResults) error {
|
||||
var builder *buildah.Builder
|
||||
|
||||
if len(args) == 0 {
|
||||
return errors.New("container or image name must be specified")
|
||||
}
|
||||
if err := buildahcli.VerifyFlagsArgsOrder(args); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(args) > 1 {
|
||||
return errors.New("too many arguments specified")
|
||||
}
|
||||
|
||||
systemContext, err := parse.SystemContextFromOptions(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("building system context: %w", err)
|
||||
}
|
||||
|
||||
name := args[0]
|
||||
|
||||
store, err := getStore(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx := getContext()
|
||||
|
||||
switch iopts.inspectType {
|
||||
case inspectTypeContainer, inspectTypeApp:
|
||||
builder, err = openBuilder(ctx, store, name)
|
||||
if err != nil {
|
||||
if flagChanged(c, "type") {
|
||||
return fmt.Errorf("reading build container: %w", err)
|
||||
}
|
||||
builder, err = openImage(ctx, systemContext, store, name)
|
||||
if err != nil {
|
||||
if manifestErr := manifestInspect(ctx, store, systemContext, name); manifestErr == nil {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
case inspectTypeImage:
|
||||
builder, err = openImage(ctx, systemContext, store, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case inspectTypeManifest:
|
||||
return manifestInspect(ctx, store, systemContext, name)
|
||||
default:
|
||||
return fmt.Errorf("the only recognized types are %q and %q", inspectTypeContainer, inspectTypeImage)
|
||||
}
|
||||
out := buildah.GetBuildInfo(builder)
|
||||
if iopts.format != "" {
|
||||
format := iopts.format
|
||||
if matched, err := regexp.MatchString("{{.*}}", format); err != nil {
|
||||
return fmt.Errorf("validating format provided: %s: %w", format, err)
|
||||
} else if !matched {
|
||||
return fmt.Errorf("invalid format provided: %s", format)
|
||||
}
|
||||
t, err := template.New("format").Parse(format)
|
||||
if err != nil {
|
||||
return fmt.Errorf("template parsing error: %w", err)
|
||||
}
|
||||
if err = t.Execute(os.Stdout, out); err != nil {
|
||||
return err
|
||||
}
|
||||
if term.IsTerminal(int(os.Stdout.Fd())) {
|
||||
fmt.Println()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
enc.SetIndent("", " ")
|
||||
if term.IsTerminal(int(os.Stdout.Fd())) {
|
||||
enc.SetEscapeHTML(false)
|
||||
}
|
||||
return enc.Encode(out)
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
// Copyright © 2022 sealos.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package buildah
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/buildah"
|
||||
"github.com/containers/buildah/pkg/parse"
|
||||
"github.com/containers/image/v5/types"
|
||||
"github.com/containers/storage"
|
||||
storagetypes "github.com/containers/storage/types"
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
)
|
||||
|
||||
type Interface interface {
|
||||
Pull(v1.Platform, string, ...string) error
|
||||
Load(string) (string, error)
|
||||
InspectImage(string) (v1.Image, error)
|
||||
Create(string, string) (buildah.BuilderInfo, error)
|
||||
Delete(string) error
|
||||
InspectContainer(string) (buildah.BuilderInfo, error)
|
||||
ListContainers() ([]JSONContainer, error)
|
||||
}
|
||||
|
||||
func New(id string) (Interface, error) {
|
||||
systemContext, err := parse.SystemContextFromOptions(rootCmd)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("building system context: %w", err)
|
||||
}
|
||||
store, err := getStore(rootCmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &realImpl{
|
||||
id: id,
|
||||
store: store,
|
||||
systemContext: systemContext,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type realImpl struct {
|
||||
id string // as identity prefix
|
||||
store storage.Store
|
||||
systemContext *types.SystemContext
|
||||
}
|
||||
|
||||
func (impl *realImpl) Pull(pf v1.Platform, pullPolicy string, imageNames ...string) error {
|
||||
iopt := newDefaultPullOptions()
|
||||
iopt.os = pf.OS
|
||||
iopt.arch = pf.Architecture
|
||||
iopt.variant = pf.Variant
|
||||
iopt.pullPolicy = pullPolicy
|
||||
ids, err := doPull(impl.store, impl.systemContext, imageNames, iopt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Info("images %s are pulled", strings.Join(ids, ", "))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (impl *realImpl) InspectImage(name string) (v1.Image, error) {
|
||||
builder, err := openImage(getContext(), impl.systemContext, impl.store, name)
|
||||
if err != nil {
|
||||
return v1.Image{}, err
|
||||
}
|
||||
out := buildah.GetBuildInfo(builder)
|
||||
return out.OCIv1, nil
|
||||
}
|
||||
|
||||
func (impl *realImpl) Create(name string, image string) (buildah.BuilderInfo, error) {
|
||||
if err := impl.Delete(impl.finalizeName(name)); err != nil {
|
||||
return buildah.BuilderInfo{}, fmt.Errorf("failed to delete: %v", err)
|
||||
}
|
||||
if _, err := impl.from(impl.finalizeName(name), image); err != nil {
|
||||
return buildah.BuilderInfo{}, fmt.Errorf("failed to from: %v", err)
|
||||
}
|
||||
if _, err := impl.mount(impl.finalizeName(name)); err != nil {
|
||||
return buildah.BuilderInfo{}, fmt.Errorf("failed to mount: %v", err)
|
||||
}
|
||||
return impl.InspectContainer(name)
|
||||
}
|
||||
|
||||
func (impl *realImpl) Delete(name string) error {
|
||||
builder, err := openBuilder(getContext(), impl.store, impl.finalizeName(name))
|
||||
if err != nil {
|
||||
if err == storagetypes.ErrContainerUnknown {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return builder.Delete()
|
||||
}
|
||||
|
||||
func (impl *realImpl) from(name, image string) (*buildah.Builder, error) {
|
||||
cmd := impl.mockCmd()
|
||||
opts := newDefaultFromReply()
|
||||
opts.name = impl.finalizeName(name)
|
||||
opts.pull = ""
|
||||
opts.RegisterFlags(cmd.Flags())
|
||||
return doFrom(cmd, image, opts, impl.store, impl.systemContext)
|
||||
}
|
||||
|
||||
func (impl *realImpl) mount(name string) (jsonMount, error) {
|
||||
jsonMounts, err := doMounts(impl.store, []string{name})
|
||||
if err != nil {
|
||||
return jsonMount{}, err
|
||||
}
|
||||
return jsonMounts[0], nil
|
||||
}
|
||||
|
||||
func (impl *realImpl) mockCmd() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "mock",
|
||||
}
|
||||
}
|
||||
|
||||
func (impl *realImpl) InspectContainer(name string) (buildah.BuilderInfo, error) {
|
||||
builder, err := openBuilder(getContext(), impl.store, impl.finalizeName(name))
|
||||
if err != nil {
|
||||
return buildah.BuilderInfo{}, err
|
||||
}
|
||||
return buildah.GetBuildInfo(builder), nil
|
||||
}
|
||||
|
||||
func (impl *realImpl) finalizeName(name string) string {
|
||||
if strings.HasPrefix(name, impl.id) {
|
||||
return name
|
||||
}
|
||||
return fmt.Sprintf("%s-%s", impl.id, name)
|
||||
}
|
||||
|
||||
func (impl *realImpl) ListContainers() ([]JSONContainer, error) {
|
||||
opts := containerOptions{
|
||||
json: true,
|
||||
noHeading: true,
|
||||
}
|
||||
params, err := parseCtrFilter(fmt.Sprintf("name=%s", impl.id))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing filter: %w", err)
|
||||
}
|
||||
_, jsonContainers, err := readContainers(impl.store, opts, params)
|
||||
return jsonContainers, err
|
||||
}
|
||||
|
||||
func (impl *realImpl) Load(input string) (string, error) {
|
||||
ids, err := doPull(impl.store, impl.systemContext, []string{fmt.Sprintf("%s:%s", DockerArchive, input)}, newDefaultPullOptions())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
fmt.Printf("%s\n", ids[0])
|
||||
return ids[0], nil
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright © 2022 buildah.
|
||||
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/containers/buildah/define"
|
||||
encconfig "github.com/containers/ocicrypt/config"
|
||||
enchelpers "github.com/containers/ocicrypt/helpers"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func DecryptConfig(decryptionKeys []string) (*encconfig.DecryptConfig, error) {
|
||||
decConfig := &encconfig.DecryptConfig{}
|
||||
if len(decryptionKeys) > 0 {
|
||||
// decryption
|
||||
dcc, err := enchelpers.CreateCryptoConfig([]string{}, decryptionKeys)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "invalid decryption keys")
|
||||
}
|
||||
cc := encconfig.CombineCryptoConfigs([]encconfig.CryptoConfig{dcc})
|
||||
decConfig = cc.DecryptConfig
|
||||
}
|
||||
|
||||
return decConfig, nil
|
||||
}
|
||||
|
||||
// EncryptConfig translates encryptionKeys into a EncriptionsConfig structure
|
||||
func EncryptConfig(encryptionKeys []string, encryptLayers []int) (*encconfig.EncryptConfig, *[]int, error) {
|
||||
var encLayers *[]int
|
||||
var encConfig *encconfig.EncryptConfig
|
||||
|
||||
if len(encryptionKeys) > 0 {
|
||||
// encryption
|
||||
encLayers = &encryptLayers
|
||||
ecc, err := enchelpers.CreateCryptoConfig(encryptionKeys, []string{})
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("invalid encryption keys: %w", err)
|
||||
}
|
||||
cc := encconfig.CombineCryptoConfigs([]encconfig.CryptoConfig{ecc})
|
||||
encConfig = cc.EncryptConfig
|
||||
}
|
||||
return encConfig, encLayers, nil
|
||||
}
|
||||
|
||||
// GetFormat translates format string into either docker or OCI format constant
|
||||
func GetFormat(format string) (string, error) {
|
||||
switch format {
|
||||
case define.OCI:
|
||||
return define.OCIv1ImageManifest, nil
|
||||
case define.DOCKER:
|
||||
return define.Dockerv2ImageManifest, nil
|
||||
default:
|
||||
return "", fmt.Errorf("unrecognized image type %q", format)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright © 2022 buildah.
|
||||
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 buildah
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/containers/buildah/pkg/parse"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newLoadCommand() *cobra.Command {
|
||||
var (
|
||||
opts = newDefaultPullOptions()
|
||||
archiveName string
|
||||
)
|
||||
|
||||
loadCommand := &cobra.Command{
|
||||
Use: "load",
|
||||
Short: "Load image from archive file",
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
return pullCmd(cmd, []string{fmt.Sprintf("%s:%s", DockerArchive, archiveName)}, opts)
|
||||
},
|
||||
Example: fmt.Sprintf(`%[1]s load -i kubernetes.tar`, rootCmd.Name()),
|
||||
}
|
||||
loadCommand.SetUsageTemplate(UsageTemplate())
|
||||
fs := loadCommand.Flags()
|
||||
fs.String("os", runtime.GOOS, "prefer `OS` instead of the running OS for choosing images")
|
||||
fs.String("arch", runtime.GOARCH, "prefer `ARCH` instead of the architecture of the machine for choosing images")
|
||||
fs.StringSlice("platform", []string{parse.DefaultPlatform()}, "prefer OS/ARCH instead of the current operating system and architecture for choosing images")
|
||||
fs.String("variant", "", "override the `variant` of the specified image")
|
||||
fs.StringVarP(&archiveName, "input", "i", "", "load image from tar archive file")
|
||||
_ = markFlagsHidden(fs, "os", "arch", "platform", "variant")
|
||||
_ = loadCommand.MarkFlagRequired("input")
|
||||
return loadCommand
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
// Copyright © 2022 buildah.
|
||||
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 buildah
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/containers/buildah/pkg/parse"
|
||||
"github.com/containers/common/pkg/auth"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
type loginReply struct {
|
||||
loginOpts auth.LoginOptions
|
||||
getLogin bool
|
||||
tlsVerify bool
|
||||
}
|
||||
|
||||
func newDefaultLoginReply() loginReply {
|
||||
return loginReply{
|
||||
loginOpts: auth.LoginOptions{
|
||||
Stdin: os.Stdin,
|
||||
Stdout: os.Stdout,
|
||||
AcceptRepositories: true,
|
||||
},
|
||||
getLogin: true,
|
||||
tlsVerify: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (opts *loginReply) RegisterFlags(fs *pflag.FlagSet) {
|
||||
fs.SetInterspersed(false)
|
||||
fs.BoolVar(&opts.tlsVerify, "tls-verify", opts.getLogin, "require HTTPS and verify certificates when accessing the registry. TLS verification cannot be used when talking to an insecure registry.")
|
||||
fs.BoolVar(&opts.getLogin, "get-login", opts.tlsVerify, "return the current login user for the registry")
|
||||
fs.AddFlagSet(auth.GetLoginFlags(&opts.loginOpts))
|
||||
}
|
||||
|
||||
func newLoginCommand() *cobra.Command {
|
||||
var (
|
||||
opts = newDefaultLoginReply()
|
||||
loginDescription = "Login to a container registry on a specified server."
|
||||
)
|
||||
loginCommand := &cobra.Command{
|
||||
Use: "login",
|
||||
Short: "Login to a container registry",
|
||||
Long: loginDescription,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return loginCmd(cmd, args, &opts)
|
||||
},
|
||||
Example: fmt.Sprintf(`%s login quay.io`, rootCmd.Name()),
|
||||
}
|
||||
loginCommand.SetUsageTemplate(UsageTemplate())
|
||||
|
||||
opts.RegisterFlags(loginCommand.Flags())
|
||||
return loginCommand
|
||||
}
|
||||
|
||||
func loginCmd(c *cobra.Command, args []string, iopts *loginReply) error {
|
||||
if len(args) > 1 {
|
||||
return errors.New("too many arguments, login takes only 1 argument")
|
||||
}
|
||||
if len(args) == 0 {
|
||||
return errors.New("please specify a registry to login to")
|
||||
}
|
||||
|
||||
if err := setXDGRuntimeDir(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
systemContext, err := parse.SystemContextFromOptions(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("building system context: %w", err)
|
||||
}
|
||||
ctx := getContext()
|
||||
iopts.loginOpts.GetLoginSet = flagChanged(c, "get-login")
|
||||
return auth.Login(ctx, systemContext, &iopts.loginOpts, args)
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright © 2022 buildah.
|
||||
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 buildah
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/containers/buildah/pkg/parse"
|
||||
"github.com/containers/common/pkg/auth"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newLogoutCommand() *cobra.Command {
|
||||
var (
|
||||
opts = auth.LogoutOptions{
|
||||
Stdout: os.Stdout,
|
||||
AcceptRepositories: true,
|
||||
}
|
||||
logoutDescription = "Remove the cached username and password for the registry."
|
||||
)
|
||||
logoutCommand := &cobra.Command{
|
||||
Use: "logout",
|
||||
Short: "Logout of a container registry",
|
||||
Long: logoutDescription,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return logoutCmd(cmd, args, &opts)
|
||||
},
|
||||
Example: fmt.Sprintf(`%s logout quay.io`, rootCmd.Name()),
|
||||
}
|
||||
logoutCommand.SetUsageTemplate(UsageTemplate())
|
||||
|
||||
flags := auth.GetLogoutFlags(&opts)
|
||||
flags.SetInterspersed(false)
|
||||
logoutCommand.Flags().AddFlagSet(flags)
|
||||
return logoutCommand
|
||||
}
|
||||
|
||||
func logoutCmd(c *cobra.Command, args []string, iopts *auth.LogoutOptions) error {
|
||||
if len(args) > 1 {
|
||||
return errors.New("too many arguments, logout takes at most 1 argument")
|
||||
}
|
||||
if len(args) == 0 && !iopts.All {
|
||||
return errors.New("registry must be given")
|
||||
}
|
||||
|
||||
if err := setXDGRuntimeDir(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
systemContext, err := parse.SystemContextFromOptions(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("building system context: %w", err)
|
||||
}
|
||||
return auth.Logout(systemContext, iopts, args)
|
||||
}
|
||||
@@ -0,0 +1,940 @@
|
||||
// Copyright © 2022 buildah.
|
||||
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 buildah
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/buildah/pkg/cli"
|
||||
"github.com/containers/buildah/pkg/parse"
|
||||
"github.com/containers/buildah/util"
|
||||
"github.com/containers/common/libimage"
|
||||
"github.com/containers/common/libimage/manifests"
|
||||
"github.com/containers/common/pkg/auth"
|
||||
cp "github.com/containers/image/v5/copy"
|
||||
"github.com/containers/image/v5/manifest"
|
||||
"github.com/containers/image/v5/transports"
|
||||
"github.com/containers/image/v5/transports/alltransports"
|
||||
"github.com/containers/image/v5/types"
|
||||
"github.com/containers/storage"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
)
|
||||
|
||||
type manifestCreateOpts struct {
|
||||
os, arch string
|
||||
all, tlsVerify, insecure, amend bool
|
||||
}
|
||||
|
||||
func (opts *manifestCreateOpts) RegisterFlags(fs *pflag.FlagSet) error {
|
||||
fs.BoolVar(&opts.all, "all", false, "add all of the lists' images if the images to add are lists")
|
||||
fs.BoolVar(&opts.amend, "amend", false, "modify an existing list if one with the desired name already exists")
|
||||
fs.StringVar(&opts.os, "os", "", "if any of the specified images is a list, choose the one for `os`")
|
||||
fs.StringVar(&opts.arch, "arch", "", "if any of the specified images is a list, choose the one for `arch`")
|
||||
fs.BoolVar(&opts.insecure, "insecure", false, "neither require HTTPS nor verify certificates when accessing the registry. TLS verification cannot be used when talking to an insecure registry.")
|
||||
fs.BoolVar(&opts.tlsVerify, "tls-verify", true, "require HTTPS and verify certificates when accessing the registry. TLS verification cannot be used when talking to an insecure registry.")
|
||||
fs.SetNormalizeFunc(cli.AliasFlags)
|
||||
return markFlagsHidden(fs, []string{"os", "arch", "insecure"}...)
|
||||
}
|
||||
|
||||
type manifestAddOpts struct {
|
||||
authfile, certDir, creds, os, arch, variant, osVersion string
|
||||
features, osFeatures, annotations []string
|
||||
tlsVerify, insecure, all bool
|
||||
}
|
||||
|
||||
func (opts *manifestAddOpts) RegisterFlags(fs *pflag.FlagSet) error {
|
||||
fs.StringVar(&opts.authfile, "authfile", auth.GetDefaultAuthFile(), "path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
|
||||
fs.StringVar(&opts.certDir, "cert-dir", "", "use certificates at the specified path to access the registry")
|
||||
fs.StringVar(&opts.creds, "creds", "", "use `[username[:password]]` for accessing the registry")
|
||||
fs.StringVar(&opts.os, "os", "", "override the `OS` of the specified image")
|
||||
fs.StringVar(&opts.arch, "arch", "", "override the `architecture` of the specified image")
|
||||
fs.StringVar(&opts.variant, "variant", "", "override the `variant` of the specified image")
|
||||
fs.StringVar(&opts.osVersion, "os-version", "", "override the OS `version` of the specified image")
|
||||
fs.StringSliceVar(&opts.features, "features", nil, "override the `features` of the specified image")
|
||||
fs.StringSliceVar(&opts.osFeatures, "os-features", nil, "override the OS `features` of the specified image")
|
||||
fs.StringSliceVar(&opts.annotations, "annotation", nil, "set an `annotation` for the specified image")
|
||||
fs.BoolVar(&opts.insecure, "insecure", false, "neither require HTTPS nor verify certificates when accessing the registry. TLS verification cannot be used when talking to an insecure registry.")
|
||||
fs.BoolVar(&opts.tlsVerify, "tls-verify", true, "require HTTPS and verify certificates when accessing the registry. TLS verification cannot be used when talking to an insecure registry.")
|
||||
fs.BoolVar(&opts.all, "all", false, "add all of the list's images if the image is a list")
|
||||
fs.SetNormalizeFunc(cli.AliasFlags)
|
||||
return markFlagsHidden(fs, []string{"insecure"}...)
|
||||
}
|
||||
|
||||
type manifestRemoveOpts struct{}
|
||||
|
||||
type manifestAnnotateOpts struct {
|
||||
os, arch, variant, osVersion string
|
||||
features, osFeatures, annotations []string
|
||||
}
|
||||
|
||||
func (opts *manifestAnnotateOpts) RegisterFlags(fs *pflag.FlagSet) error {
|
||||
fs.StringVar(&opts.os, "os", "", "override the `OS` of the specified image")
|
||||
fs.StringVar(&opts.arch, "arch", "", "override the `Architecture` of the specified image")
|
||||
fs.StringVar(&opts.variant, "variant", "", "override the `Variant` of the specified image")
|
||||
fs.StringVar(&opts.osVersion, "os-version", "", "override the os `version` of the specified image")
|
||||
fs.StringSliceVar(&opts.features, "features", nil, "override the `features` of the specified image")
|
||||
fs.StringSliceVar(&opts.osFeatures, "os-features", nil, "override the os `features` of the specified image")
|
||||
fs.StringSliceVar(&opts.annotations, "annotation", nil, "set an `annotation` for the specified image")
|
||||
return nil
|
||||
}
|
||||
|
||||
type manifestInspectOpts struct{}
|
||||
|
||||
func newManifestCommand() *cobra.Command {
|
||||
var (
|
||||
manifestDescription = "\n Creates, modifies, and pushes manifest lists and image indexes."
|
||||
manifestCreateDescription = "\n Creates manifest lists and image indexes."
|
||||
manifestAddDescription = "\n Adds an image to a manifest list or image index."
|
||||
manifestRemoveDescription = "\n Removes an image from a manifest list or image index."
|
||||
manifestAnnotateDescription = "\n Adds or updates information about an entry in a manifest list or image index."
|
||||
manifestInspectDescription = "\n Display the contents of a manifest list or image index."
|
||||
manifestPushDescription = "\n Pushes manifest lists and image indexes to registries."
|
||||
manifestRmDescription = "\n Remove one or more manifest lists from local storage."
|
||||
manifestExistsDescription = "\n Check if a manifest list exists in local storage."
|
||||
manifestCreateOpts manifestCreateOpts
|
||||
manifestAddOpts manifestAddOpts
|
||||
manifestRemoveOpts manifestRemoveOpts
|
||||
manifestAnnotateOpts manifestAnnotateOpts
|
||||
manifestInspectOpts manifestInspectOpts
|
||||
manifestPushOpts pushOptions
|
||||
)
|
||||
manifestCommand := &cobra.Command{
|
||||
Use: "manifest",
|
||||
Hidden: true,
|
||||
Short: "Manipulate manifest lists and image indexes",
|
||||
Long: manifestDescription,
|
||||
Example: fmt.Sprintf(`%[1]s manifest create localhost/list
|
||||
%[1]s manifest add localhost/list localhost/image
|
||||
%[1]s manifest annotate --annotation A=B localhost/list localhost/image
|
||||
%[1]s manifest annotate --annotation A=B localhost/list sha256:entryManifestDigest
|
||||
%[1]s manifest inspect localhost/list
|
||||
%[1]s manifest push localhost/list transport:destination
|
||||
%[1]s manifest remove localhost/list sha256:entryManifestDigest
|
||||
%[1]s manifest rm localhost/list`, rootCmd.Name()),
|
||||
}
|
||||
manifestCommand.SetUsageTemplate(UsageTemplate())
|
||||
|
||||
manifestCreateCommand := &cobra.Command{
|
||||
Use: "create",
|
||||
Short: "Create manifest list or image index",
|
||||
Long: manifestCreateDescription,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return manifestCreateCmd(cmd, args, manifestCreateOpts)
|
||||
},
|
||||
Example: fmt.Sprintf(`%[1]s manifest create mylist:v1.11
|
||||
%[1]s manifest create mylist:v1.11 arch-specific-image-to-add
|
||||
%[1]s manifest create --all mylist:v1.11 transport:tagged-image-to-add`, rootCmd.Name()),
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
}
|
||||
manifestCreateCommand.SetUsageTemplate(UsageTemplate())
|
||||
err := manifestCreateOpts.RegisterFlags(manifestCreateCommand.Flags())
|
||||
bailOnError(err, "failed to register manifest create option flags")
|
||||
manifestCommand.AddCommand(manifestCreateCommand)
|
||||
|
||||
manifestAddCommand := &cobra.Command{
|
||||
Use: "add",
|
||||
Short: "Add images to a manifest list or image index",
|
||||
Long: manifestAddDescription,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return manifestAddCmd(cmd, args, manifestAddOpts)
|
||||
},
|
||||
Example: fmt.Sprintf(`%[1]s manifest add mylist:v1.11 image:v1.11-amd64
|
||||
%[1]s manifest add mylist:v1.11 transport:imageName`, rootCmd.Name()),
|
||||
Args: cobra.MinimumNArgs(2),
|
||||
}
|
||||
manifestAddCommand.SetUsageTemplate(UsageTemplate())
|
||||
err = manifestAddOpts.RegisterFlags(manifestAddCommand.Flags())
|
||||
bailOnError(err, "failed to register manifest add option flags")
|
||||
manifestCommand.AddCommand(manifestAddCommand)
|
||||
|
||||
manifestRemoveCommand := &cobra.Command{
|
||||
Use: "remove",
|
||||
Short: "Remove an entry from a manifest list or image index",
|
||||
Long: manifestRemoveDescription,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return manifestRemoveCmd(cmd, args, manifestRemoveOpts)
|
||||
},
|
||||
Example: fmt.Sprintf(`%s manifest remove mylist:v1.11 sha256:15352d97781ffdf357bf3459c037be3efac4133dc9070c2dce7eca7c05c3e736`, rootCmd.Name()),
|
||||
Args: cobra.MinimumNArgs(2),
|
||||
}
|
||||
manifestRemoveCommand.SetUsageTemplate(UsageTemplate())
|
||||
manifestCommand.AddCommand(manifestRemoveCommand)
|
||||
|
||||
manifestExistsCommand := &cobra.Command{
|
||||
Use: "exists",
|
||||
Short: "Check if a manifest list exists in local storage",
|
||||
Long: manifestExistsDescription,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return manifestExistsCmd(cmd, args)
|
||||
},
|
||||
Example: fmt.Sprintf(`%s manifest exists mylist`, rootCmd.Name()),
|
||||
}
|
||||
manifestExistsCommand.SetUsageTemplate(UsageTemplate())
|
||||
manifestCommand.AddCommand(manifestExistsCommand)
|
||||
|
||||
manifestAnnotateCommand := &cobra.Command{
|
||||
Use: "annotate",
|
||||
Short: "Add or update information about an entry in a manifest list or image index",
|
||||
Long: manifestAnnotateDescription,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return manifestAnnotateCmd(cmd, args, manifestAnnotateOpts)
|
||||
},
|
||||
Example: fmt.Sprintf(`%s manifest annotate --annotation left=right mylist:v1.11 image:v1.11-amd64`, rootCmd.Name()),
|
||||
Args: cobra.MinimumNArgs(2),
|
||||
}
|
||||
manifestAnnotateCommand.SetUsageTemplate(UsageTemplate())
|
||||
err = manifestAnnotateOpts.RegisterFlags(manifestAnnotateCommand.Flags())
|
||||
bailOnError(err, "failed to register manifest annotate option flags")
|
||||
manifestCommand.AddCommand(manifestAnnotateCommand)
|
||||
|
||||
manifestInspectCommand := &cobra.Command{
|
||||
Use: "inspect",
|
||||
Short: "Display the contents of a manifest list or image index",
|
||||
Long: manifestInspectDescription,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return manifestInspectCmd(cmd, args, manifestInspectOpts)
|
||||
},
|
||||
Example: fmt.Sprintf(`%s manifest inspect mylist:v1.11`, rootCmd.Name()),
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
}
|
||||
manifestInspectCommand.SetUsageTemplate(UsageTemplate())
|
||||
manifestCommand.AddCommand(manifestInspectCommand)
|
||||
|
||||
manifestPushCommand := &cobra.Command{
|
||||
Use: "push",
|
||||
Short: "Push a manifest list or image index to a registry",
|
||||
Long: manifestPushDescription,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return manifestPushCmd(cmd, args, manifestPushOpts)
|
||||
},
|
||||
Example: fmt.Sprintf(`%s manifest push mylist:v1.11 transport:imageName`, rootCmd.Name()),
|
||||
Args: cobra.MinimumNArgs(2),
|
||||
}
|
||||
manifestPushCommand.SetUsageTemplate(UsageTemplate())
|
||||
fs := manifestPushCommand.Flags()
|
||||
fs.BoolVar(&manifestPushOpts.rm, "rm", false, "remove the manifest list if push succeeds")
|
||||
fs.BoolVar(&manifestPushOpts.all, "all", false, "also push the images in the list")
|
||||
fs.StringVar(&manifestPushOpts.authfile, "authfile", auth.GetDefaultAuthFile(), "path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
|
||||
fs.StringVar(&manifestPushOpts.certDir, "cert-dir", "", "use certificates at the specified path to access the registry")
|
||||
fs.StringVar(&manifestPushOpts.creds, "creds", "", "use `[username[:password]]` for accessing the registry")
|
||||
fs.StringVar(&manifestPushOpts.digestfile, "digestfile", "", "after copying the image, write the digest of the resulting digest to the file")
|
||||
fs.StringVarP(&manifestPushOpts.format, "format", "f", "", "manifest type (oci or v2s2) to attempt to use when pushing the manifest list (default is manifest type of source)")
|
||||
fs.BoolVarP(&manifestPushOpts.removeSignatures, "remove-signatures", "", false, "don't copy signatures when pushing images")
|
||||
fs.StringVar(&manifestPushOpts.signBy, "sign-by", "", "sign the image using a GPG key with the specified `FINGERPRINT`")
|
||||
fs.StringVar(&manifestPushOpts.signaturePolicy, "signature-policy", "", "`pathname` of signature policy file (not usually used)")
|
||||
fs.BoolVar(&manifestPushOpts.insecure, "insecure", false, "neither require HTTPS nor verify certificates when accessing the registry. TLS verification cannot be used when talking to an insecure registry.")
|
||||
fs.BoolVar(&manifestPushOpts.tlsVerify, "tls-verify", true, "require HTTPS and verify certificates when accessing the registry. TLS verification cannot be used when talking to an insecure registry.")
|
||||
fs.BoolVarP(&manifestPushOpts.quiet, "quiet", "q", false, "don't output progress information when pushing lists")
|
||||
fs.SetNormalizeFunc(cli.AliasFlags)
|
||||
err = markFlagsHidden(fs, "signature-policy", "insecure")
|
||||
bailOnError(err, "")
|
||||
manifestCommand.AddCommand(manifestPushCommand)
|
||||
|
||||
manifestRmCommand := &cobra.Command{
|
||||
Use: "rm",
|
||||
Short: "Remove manifest list or image index",
|
||||
Long: manifestRmDescription,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return manifestRmCmd(cmd, args)
|
||||
},
|
||||
Example: fmt.Sprintf(`%s manifest rm mylist:v1.11`, rootCmd.Name()),
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
}
|
||||
manifestRmCommand.SetUsageTemplate(UsageTemplate())
|
||||
manifestCommand.AddCommand(manifestRmCommand)
|
||||
return manifestCommand
|
||||
}
|
||||
|
||||
func manifestExistsCmd(c *cobra.Command, args []string) error {
|
||||
if len(args) == 0 {
|
||||
return errors.New("at least a name must be specified for the list")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
store, err := getStore(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
systemContext, err := parse.SystemContextFromOptions(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("building system context: %w", err)
|
||||
}
|
||||
runtime, err := libimage.RuntimeFromStore(store, &libimage.RuntimeOptions{SystemContext: systemContext})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = runtime.LookupManifestList(name)
|
||||
if err != nil {
|
||||
if errors.Is(err, storage.ErrImageUnknown) {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func manifestCreateCmd(c *cobra.Command, args []string, opts manifestCreateOpts) error {
|
||||
if len(args) == 0 {
|
||||
return errors.New("at least a name must be specified for the list")
|
||||
}
|
||||
listImageSpec := args[0]
|
||||
imageSpecs := args[1:]
|
||||
|
||||
store, err := getStore(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
systemContext, err := parse.SystemContextFromOptions(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("building system context: %w", err)
|
||||
}
|
||||
runtime, err := libimage.RuntimeFromStore(store, &libimage.RuntimeOptions{SystemContext: systemContext})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
list := manifests.Create()
|
||||
var manifestListID string
|
||||
|
||||
names, err := util.ExpandNames([]string{listImageSpec}, systemContext, store)
|
||||
if err != nil {
|
||||
return fmt.Errorf("encountered while expanding image name %q: %w", listImageSpec, err)
|
||||
}
|
||||
if manifestListID, err = list.SaveToImage(store, "", names, manifest.DockerV2ListMediaType); err != nil {
|
||||
if errors.Is(err, storage.ErrDuplicateName) && opts.amend {
|
||||
for _, name := range names {
|
||||
manifestList, err := runtime.LookupManifestList(listImageSpec)
|
||||
if err != nil {
|
||||
logger.Debug("no list named %q found: %v", listImageSpec, err)
|
||||
continue
|
||||
}
|
||||
if _, list, err = manifests.LoadFromImage(store, manifestList.ID()); err != nil {
|
||||
logger.Debug("no list found in %q", name)
|
||||
continue
|
||||
}
|
||||
manifestListID = manifestList.ID()
|
||||
break
|
||||
}
|
||||
if list == nil {
|
||||
return fmt.Errorf("--amend specified but no matching manifest list found with name %q", listImageSpec)
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, imageSpec := range imageSpecs {
|
||||
ref, err := alltransports.ParseImageName(imageSpec)
|
||||
if err != nil {
|
||||
if ref, err = alltransports.ParseImageName(util.DefaultTransport + imageSpec); err != nil {
|
||||
// check if the local image exists
|
||||
if ref, _, err = util.FindImage(store, "", systemContext, imageSpec); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
refLocal, _, err := util.FindImage(store, "", systemContext, imageSpec)
|
||||
if err == nil {
|
||||
// Found local image so use that.
|
||||
ref = refLocal
|
||||
}
|
||||
_, err = list.Add(getContext(), systemContext, ref, opts.all)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
imageID, err := list.SaveToImage(store, manifestListID, names, manifest.DockerV2ListMediaType)
|
||||
if err == nil {
|
||||
fmt.Printf("%s\n", imageID)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func manifestAddCmd(c *cobra.Command, args []string, opts manifestAddOpts) error {
|
||||
if err := auth.CheckAuthFile(opts.authfile); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
listImageSpec := ""
|
||||
imageSpec := ""
|
||||
switch len(args) {
|
||||
case 0, 1:
|
||||
return errors.New("at least a list image and an image to add must be specified")
|
||||
case 2:
|
||||
listImageSpec = args[0]
|
||||
if listImageSpec == "" {
|
||||
return fmt.Errorf(`invalid image name "%s"`, args[0])
|
||||
}
|
||||
imageSpec = args[1]
|
||||
if imageSpec == "" {
|
||||
return fmt.Errorf(`invalid image name "%s"`, args[1])
|
||||
}
|
||||
default:
|
||||
return errors.New("at least two arguments are necessary: list and image to add to list")
|
||||
}
|
||||
|
||||
store, err := getStore(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
systemContext, err := parse.SystemContextFromOptions(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("building system context: %w", err)
|
||||
}
|
||||
runtime, err := libimage.RuntimeFromStore(store, &libimage.RuntimeOptions{SystemContext: systemContext})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
manifestList, err := runtime.LookupManifestList(listImageSpec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, list, err := manifests.LoadFromImage(store, manifestList.ID())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ref, err := alltransports.ParseImageName(imageSpec)
|
||||
if err != nil {
|
||||
if ref, err = alltransports.ParseImageName(util.DefaultTransport + imageSpec); err != nil {
|
||||
// check if the local image exists
|
||||
if ref, _, err = util.FindImage(store, "", systemContext, imageSpec); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
digest, err := list.Add(getContext(), systemContext, ref, opts.all)
|
||||
if err != nil {
|
||||
var storeErr error
|
||||
// Retry without a custom system context. A user may want to add
|
||||
// a custom platform (see #3511).
|
||||
if ref, _, storeErr = util.FindImage(store, "", nil, imageSpec); storeErr != nil {
|
||||
logger.Error("Error while trying to find image on local storage: %v", storeErr)
|
||||
return err
|
||||
}
|
||||
digest, storeErr = list.Add(getContext(), systemContext, ref, opts.all)
|
||||
if storeErr != nil {
|
||||
logger.Error("Error while trying to add on manifest list: %v", storeErr)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if opts.os != "" {
|
||||
if err := list.SetOS(digest, opts.os); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if opts.osVersion != "" {
|
||||
if err := list.SetOSVersion(digest, opts.osVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(opts.osFeatures) != 0 {
|
||||
if err := list.SetOSFeatures(digest, opts.osFeatures); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if opts.arch != "" {
|
||||
if err := list.SetArchitecture(digest, opts.arch); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if opts.variant != "" {
|
||||
if err := list.SetVariant(digest, opts.variant); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(opts.features) != 0 {
|
||||
if err := list.SetFeatures(digest, opts.features); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(opts.annotations) != 0 {
|
||||
annotations := make(map[string]string)
|
||||
for _, annotationSpec := range opts.annotations {
|
||||
spec := strings.SplitN(annotationSpec, "=", 2)
|
||||
if len(spec) != 2 {
|
||||
return fmt.Errorf("no value given for annotation %q", spec[0])
|
||||
}
|
||||
annotations[spec[0]] = spec[1]
|
||||
}
|
||||
if err := list.SetAnnotations(&digest, annotations); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
updatedListID, err := list.SaveToImage(store, manifestList.ID(), nil, "")
|
||||
if err == nil {
|
||||
fmt.Printf("%s: %s\n", updatedListID, digest.String())
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func manifestRemoveCmd(c *cobra.Command, args []string, opts manifestRemoveOpts) error {
|
||||
listImageSpec := ""
|
||||
var instanceDigest digest.Digest
|
||||
switch len(args) {
|
||||
case 0, 1:
|
||||
return errors.New("at least a list image and one or more instance digests must be specified")
|
||||
case 2:
|
||||
listImageSpec = args[0]
|
||||
if listImageSpec == "" {
|
||||
return fmt.Errorf(`invalid image name "%s"`, args[0])
|
||||
}
|
||||
instanceSpec := args[1]
|
||||
if instanceSpec == "" {
|
||||
return fmt.Errorf(`invalid instance "%s"`, args[1])
|
||||
}
|
||||
d, err := digest.Parse(instanceSpec)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`invalid instance "%s": %v`, args[1], err)
|
||||
}
|
||||
instanceDigest = d
|
||||
default:
|
||||
return errors.New("at least two arguments are necessary: list and digest of instance to remove from list")
|
||||
}
|
||||
|
||||
store, err := getStore(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
systemContext, err := parse.SystemContextFromOptions(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("building system context: %w", err)
|
||||
}
|
||||
|
||||
runtime, err := libimage.RuntimeFromStore(store, &libimage.RuntimeOptions{SystemContext: systemContext})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
manifestList, err := runtime.LookupManifestList(listImageSpec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := manifestList.RemoveInstance(instanceDigest); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("%s: %s\n", manifestList.ID(), instanceDigest.String())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func manifestRmCmd(c *cobra.Command, args []string) error {
|
||||
store, err := getStore(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
systemContext, err := parse.SystemContextFromOptions(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("building system context: %w", err)
|
||||
}
|
||||
|
||||
runtime, err := libimage.RuntimeFromStore(store, &libimage.RuntimeOptions{SystemContext: systemContext})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
options := &libimage.RemoveImagesOptions{
|
||||
Filters: []string{"readonly=false"},
|
||||
LookupManifest: true,
|
||||
}
|
||||
rmiReports, rmiErrors := runtime.RemoveImages(context.Background(), args, options)
|
||||
for _, r := range rmiReports {
|
||||
for _, u := range r.Untagged {
|
||||
fmt.Printf("untagged: %s\n", u)
|
||||
}
|
||||
}
|
||||
for _, r := range rmiReports {
|
||||
if r.Removed {
|
||||
fmt.Printf("%s\n", r.ID)
|
||||
}
|
||||
}
|
||||
|
||||
var multiE *multierror.Error
|
||||
multiE = multierror.Append(multiE, rmiErrors...)
|
||||
return multiE.ErrorOrNil()
|
||||
}
|
||||
|
||||
func manifestAnnotateCmd(c *cobra.Command, args []string, opts manifestAnnotateOpts) error {
|
||||
listImageSpec := ""
|
||||
imageSpec := ""
|
||||
switch len(args) {
|
||||
case 0:
|
||||
return errors.New("at least a list image must be specified")
|
||||
case 1:
|
||||
listImageSpec = args[0]
|
||||
if listImageSpec == "" {
|
||||
return fmt.Errorf(`invalid image name "%s"`, args[0])
|
||||
}
|
||||
case 2:
|
||||
listImageSpec = args[0]
|
||||
if listImageSpec == "" {
|
||||
return fmt.Errorf(`invalid image name "%s"`, args[0])
|
||||
}
|
||||
imageSpec = args[1]
|
||||
if imageSpec == "" {
|
||||
return fmt.Errorf(`invalid image name "%s"`, args[1])
|
||||
}
|
||||
default:
|
||||
return errors.New("at least two arguments are necessary: list and image to add to list")
|
||||
}
|
||||
|
||||
store, err := getStore(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
systemContext, err := parse.SystemContextFromOptions(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("building system context: %w", err)
|
||||
}
|
||||
runtime, err := libimage.RuntimeFromStore(store, &libimage.RuntimeOptions{SystemContext: systemContext})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
manifestList, err := runtime.LookupManifestList(listImageSpec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, list, err := manifests.LoadFromImage(store, manifestList.ID())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
digest, err := digest.Parse(imageSpec)
|
||||
if err != nil {
|
||||
ctx := getContext()
|
||||
ref, _, err := util.FindImage(store, "", systemContext, imageSpec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
img, err := ref.NewImageSource(ctx, systemContext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer img.Close()
|
||||
manifestBytes, _, err := img.GetManifest(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
digest, err = manifest.Digest(manifestBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if opts.os != "" {
|
||||
if err := list.SetOS(digest, opts.os); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if opts.osVersion != "" {
|
||||
if err := list.SetOSVersion(digest, opts.osVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(opts.osFeatures) != 0 {
|
||||
if err := list.SetOSFeatures(digest, opts.osFeatures); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if opts.arch != "" {
|
||||
if err := list.SetArchitecture(digest, opts.arch); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if opts.variant != "" {
|
||||
if err := list.SetVariant(digest, opts.variant); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(opts.features) != 0 {
|
||||
if err := list.SetFeatures(digest, opts.features); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(opts.annotations) != 0 {
|
||||
annotations := make(map[string]string)
|
||||
for _, annotationSpec := range opts.annotations {
|
||||
spec := strings.SplitN(annotationSpec, "=", 2)
|
||||
if len(spec) != 2 {
|
||||
return fmt.Errorf("no value given for annotation %q", spec[0])
|
||||
}
|
||||
annotations[spec[0]] = spec[1]
|
||||
}
|
||||
if err := list.SetAnnotations(&digest, annotations); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
updatedListID, err := list.SaveToImage(store, manifestList.ID(), nil, "")
|
||||
if err == nil {
|
||||
fmt.Printf("%s: %s\n", updatedListID, digest.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func manifestInspectCmd(c *cobra.Command, args []string, opts manifestInspectOpts) error {
|
||||
imageSpec := ""
|
||||
switch len(args) {
|
||||
case 0:
|
||||
return errors.New("at least a source list ID must be specified")
|
||||
case 1:
|
||||
imageSpec = args[0]
|
||||
if imageSpec == "" {
|
||||
return fmt.Errorf(`invalid image name "%s"`, imageSpec)
|
||||
}
|
||||
default:
|
||||
return errors.New("only one argument is necessary for inspect: an image name")
|
||||
}
|
||||
|
||||
store, err := getStore(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
systemContext, err := parse.SystemContextFromOptions(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("building system context: %w", err)
|
||||
}
|
||||
|
||||
return manifestInspect(getContext(), store, systemContext, imageSpec)
|
||||
}
|
||||
|
||||
func manifestInspect(ctx context.Context, store storage.Store, systemContext *types.SystemContext, imageSpec string) error {
|
||||
runtime, err := libimage.RuntimeFromStore(store, &libimage.RuntimeOptions{SystemContext: systemContext})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printManifest := func(manifest []byte) error {
|
||||
var b bytes.Buffer
|
||||
err = json.Indent(&b, manifest, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("rendering manifest for display: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", b.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
// Before doing a remote lookup, attempt to resolve the manifest list
|
||||
// locally.
|
||||
manifestList, err := runtime.LookupManifestList(imageSpec)
|
||||
if err == nil {
|
||||
schema2List, err := manifestList.Inspect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rawSchema2List, err := json.Marshal(schema2List)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return printManifest(rawSchema2List)
|
||||
}
|
||||
if !errors.Is(err, storage.ErrImageUnknown) && !errors.Is(err, libimage.ErrNotAManifestList) {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: at some point `libimage` should support resolving manifests
|
||||
// like that. Similar to `libimage.Runtime.LookupImage` we could
|
||||
// implement a `*.LookupImageIndex`.
|
||||
refs, err := util.ResolveNameToReferences(store, systemContext, imageSpec)
|
||||
if err != nil {
|
||||
logger.Debug("error parsing reference to image %q: %v", imageSpec, err)
|
||||
}
|
||||
|
||||
if ref, _, err := util.FindImage(store, "", systemContext, imageSpec); err == nil {
|
||||
refs = append(refs, ref)
|
||||
} else if ref, err := alltransports.ParseImageName(imageSpec); err == nil {
|
||||
refs = append(refs, ref)
|
||||
}
|
||||
if len(refs) == 0 {
|
||||
return fmt.Errorf("locating images with names %v", imageSpec)
|
||||
}
|
||||
|
||||
var (
|
||||
latestErr error
|
||||
result []byte
|
||||
)
|
||||
|
||||
appendErr := func(e error) {
|
||||
if latestErr == nil {
|
||||
latestErr = e
|
||||
} else {
|
||||
latestErr = fmt.Errorf("tried %v: %w", e, latestErr)
|
||||
}
|
||||
}
|
||||
|
||||
for _, ref := range refs {
|
||||
logger.Debug("Testing reference %q for possible manifest", transports.ImageName(ref))
|
||||
|
||||
src, err := ref.NewImageSource(ctx, systemContext)
|
||||
if err != nil {
|
||||
appendErr(fmt.Errorf("reading image %q: %w", transports.ImageName(ref), err))
|
||||
continue
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
manifestBytes, manifestType, err := src.GetManifest(ctx, nil)
|
||||
if err != nil {
|
||||
appendErr(fmt.Errorf("loading manifest %q: %w", transports.ImageName(ref), err))
|
||||
continue
|
||||
}
|
||||
|
||||
if !manifest.MIMETypeIsMultiImage(manifestType) {
|
||||
appendErr(fmt.Errorf("manifest is of type %s (not a list type)", manifestType))
|
||||
continue
|
||||
}
|
||||
result = manifestBytes
|
||||
break
|
||||
}
|
||||
if len(result) == 0 && latestErr != nil {
|
||||
return latestErr
|
||||
}
|
||||
|
||||
return printManifest(result)
|
||||
}
|
||||
|
||||
func manifestPushCmd(c *cobra.Command, args []string, opts pushOptions) error {
|
||||
if err := auth.CheckAuthFile(opts.authfile); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
listImageSpec := ""
|
||||
destSpec := ""
|
||||
switch len(args) {
|
||||
case 0:
|
||||
return errors.New("at least a source list ID must be specified")
|
||||
case 1:
|
||||
return errors.New("two arguments are necessary to push: source and destination")
|
||||
case 2:
|
||||
listImageSpec = args[0]
|
||||
destSpec = args[1]
|
||||
if listImageSpec == "" {
|
||||
return fmt.Errorf(`invalid image name "%s"`, listImageSpec)
|
||||
}
|
||||
if destSpec == "" {
|
||||
return fmt.Errorf(`invalid image name "%s"`, destSpec)
|
||||
}
|
||||
default:
|
||||
return errors.New("only two arguments are necessary to push: source and destination")
|
||||
}
|
||||
|
||||
store, err := getStore(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
systemContext, err := parse.SystemContextFromOptions(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("building system context: %w", err)
|
||||
}
|
||||
|
||||
return manifestPush(systemContext, store, listImageSpec, destSpec, opts)
|
||||
}
|
||||
|
||||
func manifestPush(systemContext *types.SystemContext, store storage.Store, listImageSpec, destSpec string, opts pushOptions) error {
|
||||
runtime, err := libimage.RuntimeFromStore(store, &libimage.RuntimeOptions{SystemContext: systemContext})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
manifestList, err := runtime.LookupManifestList(listImageSpec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, list, err := manifests.LoadFromImage(store, manifestList.ID())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dest, err := alltransports.ParseImageName(destSpec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var manifestType string
|
||||
if opts.format != "" {
|
||||
switch opts.format {
|
||||
case "oci":
|
||||
manifestType = imgspecv1.MediaTypeImageManifest
|
||||
case "v2s2", "docker":
|
||||
manifestType = manifest.DockerV2Schema2MediaType
|
||||
default:
|
||||
return fmt.Errorf("unknown format %q. Choose on of the supported formats: 'oci' or 'v2s2'", opts.format)
|
||||
}
|
||||
}
|
||||
|
||||
options := manifests.PushOptions{
|
||||
Store: store,
|
||||
SystemContext: systemContext,
|
||||
ImageListSelection: cp.CopySpecificImages,
|
||||
Instances: nil,
|
||||
RemoveSignatures: opts.removeSignatures,
|
||||
SignBy: opts.signBy,
|
||||
ManifestType: manifestType,
|
||||
}
|
||||
if opts.all {
|
||||
options.ImageListSelection = cp.CopyAllImages
|
||||
}
|
||||
if !opts.quiet {
|
||||
options.ReportWriter = os.Stderr
|
||||
}
|
||||
|
||||
_, digest, err := list.Push(getContext(), dest, options)
|
||||
|
||||
if err == nil && opts.rm {
|
||||
_, err = store.DeleteImage(manifestList.ID(), true)
|
||||
}
|
||||
|
||||
if opts.digestfile != "" {
|
||||
if err = os.WriteFile(opts.digestfile, []byte(digest.String()), 0644); err != nil {
|
||||
return util.GetFailureCause(err, fmt.Errorf("failed to write digest to file %q: %w", opts.digestfile, err))
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
// Copyright © 2022 buildah.
|
||||
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 buildah
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
buildahcli "github.com/containers/buildah/pkg/cli"
|
||||
"github.com/containers/storage"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type jsonMount struct {
|
||||
Container string `json:"container,omitempty"`
|
||||
MountPoint string `json:"mountPoint"`
|
||||
}
|
||||
|
||||
type mountOptions struct {
|
||||
json bool
|
||||
}
|
||||
|
||||
func newMountCommand() *cobra.Command {
|
||||
var (
|
||||
mountDescription = fmt.Sprintf(`%[1]s mount
|
||||
mounts a working container's root filesystem for manipulation.
|
||||
`, rootCmd.Name())
|
||||
opts mountOptions
|
||||
noTruncate bool
|
||||
)
|
||||
mountCommand := &cobra.Command{
|
||||
Use: "mount",
|
||||
Hidden: true,
|
||||
Short: "Mount a working container's root filesystem",
|
||||
Long: mountDescription,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return mountCmd(cmd, args, opts)
|
||||
},
|
||||
Example: fmt.Sprintf(`%[1]s mount
|
||||
%[1]s mount containerID
|
||||
%[1]s mount containerID1 containerID2
|
||||
`, rootCmd.Name()),
|
||||
}
|
||||
mountCommand.SetUsageTemplate(UsageTemplate())
|
||||
|
||||
fs := mountCommand.Flags()
|
||||
fs.SetInterspersed(false)
|
||||
fs.BoolVar(&opts.json, "json", false, "output in JSON format")
|
||||
fs.BoolVar(&noTruncate, "notruncate", false, "do not truncate output")
|
||||
err := markFlagsHidden(fs, "notruncate")
|
||||
bailOnError(err, "")
|
||||
return mountCommand
|
||||
}
|
||||
|
||||
func mountCmd(c *cobra.Command, args []string, opts mountOptions) error {
|
||||
if err := buildahcli.VerifyFlagsArgsOrder(args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
store, err := getStore(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
jsonMounts, err := doMounts(store, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if opts.json {
|
||||
data, err := json.MarshalIndent(jsonMounts, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%s\n", data)
|
||||
} else {
|
||||
for i := range jsonMounts {
|
||||
fmt.Printf("%s %s\n", jsonMounts[i].Container, jsonMounts[i].MountPoint)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func doMounts(store storage.Store, args []string) ([]jsonMount, error) {
|
||||
var jsonMounts []jsonMount
|
||||
if len(args) > 0 {
|
||||
// Do not allow to mount a graphdriver that is not vfs if we are creating the userns as part
|
||||
// of the mount command.
|
||||
// Differently, allow the mount if we are already in a userns, as the mount point will still
|
||||
// be accessible once "buildah mount" exits.
|
||||
if os.Geteuid() != 0 && store.GraphDriverName() != "vfs" {
|
||||
return nil, fmt.Errorf("cannot mount using driver %s in rootless mode. You need to run it in a `buildah unshare` session", store.GraphDriverName())
|
||||
}
|
||||
|
||||
for _, name := range args {
|
||||
builder, err := openBuilder(getContext(), store, name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading build container %q: %w", name, err)
|
||||
}
|
||||
mountPoint, err := builder.Mount(builder.MountLabel)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("mounting %q container %q: %w", name, builder.Container, err)
|
||||
}
|
||||
jsonMounts = append(jsonMounts, jsonMount{Container: name, MountPoint: mountPoint})
|
||||
}
|
||||
} else {
|
||||
builders, err := openBuilders(store)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading build containers: %w", err)
|
||||
}
|
||||
|
||||
for _, builder := range builders {
|
||||
mounted, err := builder.Mounted()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if mounted {
|
||||
jsonMounts = append(jsonMounts, jsonMount{Container: builder.Container, MountPoint: builder.MountPoint})
|
||||
}
|
||||
}
|
||||
}
|
||||
return jsonMounts, nil
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
// Copyright © 2022 buildah.
|
||||
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 buildah
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/containers/buildah"
|
||||
"github.com/containers/buildah/define"
|
||||
buildahcli "github.com/containers/buildah/pkg/cli"
|
||||
"github.com/containers/buildah/pkg/parse"
|
||||
"github.com/containers/common/pkg/auth"
|
||||
"github.com/containers/image/v5/types"
|
||||
"github.com/containers/storage"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/labring/sealos/pkg/buildah/internal/util"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
)
|
||||
|
||||
type pullOptions struct {
|
||||
allTags bool
|
||||
authfile string
|
||||
blobCache string
|
||||
certDir string
|
||||
creds string
|
||||
signaturePolicy string
|
||||
quiet bool
|
||||
removeSignatures bool
|
||||
tlsVerify bool
|
||||
decryptionKeys []string
|
||||
pullPolicy string
|
||||
os, arch, variant string
|
||||
platform []string
|
||||
retry int
|
||||
retryDelay time.Duration
|
||||
}
|
||||
|
||||
func (opts *pullOptions) HiddenFlags() []string {
|
||||
return []string{
|
||||
"signature-policy", "blob-cache",
|
||||
}
|
||||
}
|
||||
|
||||
func newDefaultPullOptions() *pullOptions {
|
||||
return &pullOptions{
|
||||
authfile: auth.GetDefaultAuthFile(),
|
||||
pullPolicy: "missing",
|
||||
tlsVerify: true,
|
||||
os: runtime.GOOS,
|
||||
arch: runtime.GOARCH,
|
||||
platform: []string{parse.DefaultPlatform()},
|
||||
retry: buildahcli.MaxPullPushRetries,
|
||||
retryDelay: buildahcli.PullPushRetryDelay,
|
||||
}
|
||||
}
|
||||
|
||||
func (opts *pullOptions) RegisterFlags(fs *pflag.FlagSet) error {
|
||||
fs.SetInterspersed(false)
|
||||
fs.BoolVarP(&opts.allTags, "all-tags", "a", opts.allTags, "download all tagged images in the repository")
|
||||
fs.StringVar(&opts.authfile, "authfile", opts.authfile, "path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
|
||||
fs.StringVar(&opts.blobCache, "blob-cache", opts.blobCache, "store copies of pulled image blobs in the specified directory")
|
||||
fs.StringVar(&opts.certDir, "cert-dir", opts.certDir, "use certificates at the specified path to access the registry")
|
||||
fs.StringVar(&opts.creds, "creds", opts.creds, "use `[username[:password]]` for accessing the registry")
|
||||
fs.StringVar(&opts.pullPolicy, "policy", opts.pullPolicy, "missing, always, or never.")
|
||||
fs.BoolVar(&opts.removeSignatures, "remove-signatures", opts.removeSignatures, "don't copy signatures when pulling image")
|
||||
fs.StringVar(&opts.signaturePolicy, "signature-policy", opts.signaturePolicy, "`pathname` of signature policy file (not usually used)")
|
||||
fs.StringSliceVar(&opts.decryptionKeys, "decryption-key", opts.decryptionKeys, "key needed to decrypt the image")
|
||||
fs.BoolVarP(&opts.quiet, "quiet", "q", false, "don't output progress information when pulling images")
|
||||
fs.StringVar(&opts.os, "os", opts.os, "prefer `OS` instead of the running OS for choosing images")
|
||||
fs.StringVar(&opts.arch, "arch", opts.arch, "prefer `ARCH` instead of the architecture of the machine for choosing images")
|
||||
fs.StringSliceVar(&opts.platform, "platform", opts.platform, "prefer OS/ARCH instead of the current operating system and architecture for choosing images")
|
||||
fs.StringVar(&opts.variant, "variant", "", "override the `variant` of the specified image")
|
||||
fs.BoolVar(&opts.tlsVerify, "tls-verify", opts.tlsVerify, "require HTTPS and verify certificates when accessing the registry. TLS verification cannot be used when talking to an insecure registry.")
|
||||
fs.IntVar(&opts.retry, "retry", opts.retry, "number of times to retry in case of failure when performing pull")
|
||||
fs.DurationVar(&opts.retryDelay, "retry-delay", opts.retryDelay, "delay between retries in case of pull failures")
|
||||
return markFlagsHidden(fs, opts.HiddenFlags()...)
|
||||
}
|
||||
|
||||
func newPullCommand() *cobra.Command {
|
||||
var (
|
||||
opts = newDefaultPullOptions()
|
||||
|
||||
pullDescription = ` Pulls an image from a registry and stores it locally.
|
||||
An image can be pulled using its tag or digest. If a tag is not
|
||||
specified, the image with the 'latest' tag (if it exists) is pulled.`
|
||||
)
|
||||
|
||||
pullCommand := &cobra.Command{
|
||||
Use: "pull",
|
||||
Short: "Pull an image from the specified location",
|
||||
Long: pullDescription,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return pullCmd(cmd, args, opts)
|
||||
},
|
||||
Example: fmt.Sprintf(`%[1]s pull imagename
|
||||
%[1]s pull docker-daemon:imagename:imagetag
|
||||
%[1]s pull myregistry/myrepository/imagename:imagetag`, rootCmd.Name()),
|
||||
}
|
||||
pullCommand.SetUsageTemplate(UsageTemplate())
|
||||
|
||||
err := opts.RegisterFlags(pullCommand.Flags())
|
||||
bailOnError(err, "failed to register pull option flags")
|
||||
return pullCommand
|
||||
}
|
||||
|
||||
func pullCmd(c *cobra.Command, args []string, iopts *pullOptions) error {
|
||||
if len(args) == 0 {
|
||||
return errors.New("an image name must be specified")
|
||||
}
|
||||
if err := buildahcli.VerifyFlagsArgsOrder(args); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(args) > 1 {
|
||||
return errors.New("too many arguments specified")
|
||||
}
|
||||
|
||||
systemContext, err := parse.SystemContextFromOptions(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("building system context: %w", err)
|
||||
}
|
||||
platforms, err := parse.PlatformsFromOptions(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(platforms) > 1 {
|
||||
logger.Warn("ignoring platforms other than %+v: %+v", platforms[0], platforms[1:])
|
||||
}
|
||||
|
||||
store, err := getStore(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ids, err := doPull(store, systemContext, []string{args[0]}, iopts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%s\n", ids[0])
|
||||
return nil
|
||||
}
|
||||
|
||||
func doPull(store storage.Store, systemContext *types.SystemContext, imageNames []string, iopts *pullOptions) ([]string, error) {
|
||||
if err := auth.CheckAuthFile(iopts.authfile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
decConfig, err := util.DecryptConfig(iopts.decryptionKeys)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to obtain decryption config: %w", err)
|
||||
}
|
||||
|
||||
policy, ok := define.PolicyMap[iopts.pullPolicy]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unsupported pull policy %q", iopts.pullPolicy)
|
||||
}
|
||||
options := buildah.PullOptions{
|
||||
SignaturePolicyPath: iopts.signaturePolicy,
|
||||
Store: store,
|
||||
SystemContext: systemContext,
|
||||
BlobDirectory: iopts.blobCache,
|
||||
AllTags: iopts.allTags,
|
||||
ReportWriter: os.Stderr,
|
||||
RemoveSignatures: iopts.removeSignatures,
|
||||
MaxRetries: iopts.retry,
|
||||
RetryDelay: iopts.retryDelay,
|
||||
OciDecryptConfig: decConfig,
|
||||
PullPolicy: policy,
|
||||
}
|
||||
|
||||
if iopts.quiet {
|
||||
options.ReportWriter = nil // Turns off logging output
|
||||
}
|
||||
var ids []string
|
||||
for _, imageName := range imageNames {
|
||||
id, err := buildah.Pull(getContext(), imageName, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ids = append(ids, id)
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
// Copyright © 2022 buildah.
|
||||
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 buildah
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/containers/buildah"
|
||||
"github.com/containers/buildah/define"
|
||||
buildahcli "github.com/containers/buildah/pkg/cli"
|
||||
"github.com/containers/buildah/pkg/parse"
|
||||
"github.com/containers/buildah/util"
|
||||
"github.com/containers/common/pkg/auth"
|
||||
"github.com/containers/image/v5/manifest"
|
||||
"github.com/containers/image/v5/pkg/compression"
|
||||
"github.com/containers/image/v5/transports"
|
||||
"github.com/containers/image/v5/transports/alltransports"
|
||||
"github.com/containers/storage"
|
||||
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
iutil "github.com/labring/sealos/pkg/buildah/internal/util"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
)
|
||||
|
||||
type pushOptions struct {
|
||||
all bool
|
||||
authfile string
|
||||
blobCache string
|
||||
certDir string
|
||||
creds string
|
||||
digestfile string
|
||||
disableCompression bool
|
||||
format string
|
||||
compressionFormat string
|
||||
compressionLevel int
|
||||
retry int
|
||||
retryDelay time.Duration
|
||||
rm bool
|
||||
quiet bool
|
||||
removeSignatures bool
|
||||
signaturePolicy string
|
||||
signBy string
|
||||
tlsVerify bool
|
||||
encryptionKeys []string
|
||||
encryptLayers []int
|
||||
insecure bool
|
||||
}
|
||||
|
||||
func newDefaultPushOptions() *pushOptions {
|
||||
return &pushOptions{
|
||||
authfile: auth.GetDefaultAuthFile(),
|
||||
retry: buildahcli.MaxPullPushRetries,
|
||||
retryDelay: buildahcli.PullPushRetryDelay,
|
||||
tlsVerify: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (opts *pushOptions) RegisterFlags(fs *pflag.FlagSet) error {
|
||||
fs.SetInterspersed(false)
|
||||
fs.BoolVar(&opts.all, "all", opts.all, "push all of the images referenced by the manifest list")
|
||||
fs.StringVar(&opts.authfile, "authfile", opts.authfile, "path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
|
||||
fs.StringVar(&opts.blobCache, "blob-cache", opts.blobCache, "assume image blobs in the specified directory will be available for pushing")
|
||||
fs.StringVar(&opts.certDir, "cert-dir", opts.certDir, "use certificates at the specified path to access the registry")
|
||||
fs.StringVar(&opts.creds, "creds", opts.creds, "use `[username[:password]]` for accessing the registry")
|
||||
fs.StringVar(&opts.digestfile, "digestfile", opts.digestfile, "after copying the image, write the digest of the resulting image to the file")
|
||||
fs.BoolVarP(&opts.disableCompression, "disable-compression", "D", false, "don't compress layers")
|
||||
fs.StringVarP(&opts.format, "format", "f", opts.format, "manifest type (oci, v2s1, or v2s2) to use in the destination (default is manifest type of source, with fallbacks)")
|
||||
fs.StringVar(&opts.compressionFormat, "compression-format", opts.compressionFormat, "compression format to use")
|
||||
fs.IntVar(&opts.compressionLevel, "compression-level", opts.compressionLevel, "compression level to use")
|
||||
fs.BoolVarP(&opts.quiet, "quiet", "q", opts.quiet, "don't output progress information when pushing images")
|
||||
fs.IntVar(&opts.retry, "retry", opts.retry, "number of times to retry in case of failure when performing push/pull")
|
||||
fs.DurationVar(&opts.retryDelay, "retry-delay", opts.retryDelay, "delay between retries in case of push/pull failures")
|
||||
fs.BoolVar(&opts.rm, "rm", opts.rm, "remove the manifest list if push succeeds")
|
||||
fs.BoolVarP(&opts.removeSignatures, "remove-signatures", "", opts.removeSignatures, "don't copy signatures when pushing image")
|
||||
fs.StringVar(&opts.signBy, "sign-by", opts.signBy, "sign the image using a GPG key with the specified `FINGERPRINT`")
|
||||
fs.StringVar(&opts.signaturePolicy, "signature-policy", opts.signaturePolicy, "`pathname` of signature policy file (not usually used)")
|
||||
fs.StringSliceVar(&opts.encryptionKeys, "encryption-key", opts.encryptionKeys, "key with the encryption protocol to use needed to encrypt the image (e.g. jwe:/path/to/key.pem)")
|
||||
fs.IntSliceVar(&opts.encryptLayers, "encrypt-layer", opts.encryptLayers, "layers to encrypt, 0-indexed layer indices with support for negative indexing (e.g. 0 is the first layer, -1 is the last layer). If not defined, will encrypt all layers if encryption-key flag is specified")
|
||||
fs.BoolVar(&opts.tlsVerify, "tls-verify", opts.tlsVerify, "require HTTPS and verify certificates when accessing the registry. TLS verification cannot be used when talking to an insecure registry.")
|
||||
return markFlagsHidden(fs, []string{"signature-policy", "blob-cache"}...)
|
||||
}
|
||||
|
||||
func newPushCommand() *cobra.Command {
|
||||
var (
|
||||
opts = newDefaultPushOptions()
|
||||
pushDescription = fmt.Sprintf(`
|
||||
Pushes an image to a specified location.
|
||||
|
||||
The Image "DESTINATION" uses a "transport":"details" format. If not specified, will reuse source IMAGE as DESTINATION.
|
||||
|
||||
Supported transports:
|
||||
%s
|
||||
`, getListOfTransports())
|
||||
)
|
||||
|
||||
pushCommand := &cobra.Command{
|
||||
Use: "push",
|
||||
Short: "Push an image to a specified destination",
|
||||
Long: pushDescription,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return pushCmd(cmd, args, opts)
|
||||
},
|
||||
Example: fmt.Sprintf(`%[1]s push imageID docker://registry.example.com/repository:tag
|
||||
%[1]s push imageID docker-daemon:image:tagi
|
||||
%[1]s push imageID oci:/path/to/layout:image:tag`, rootCmd.Name()),
|
||||
}
|
||||
pushCommand.SetUsageTemplate(UsageTemplate())
|
||||
err := opts.RegisterFlags(pushCommand.Flags())
|
||||
bailOnError(err, "failed to register push option flags")
|
||||
return pushCommand
|
||||
}
|
||||
|
||||
func pushCmd(c *cobra.Command, args []string, iopts *pushOptions) error {
|
||||
var src, destSpec string
|
||||
|
||||
if err := buildahcli.VerifyFlagsArgsOrder(args); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := auth.CheckAuthFile(iopts.authfile); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch len(args) {
|
||||
case 0:
|
||||
return errors.New("at least a source image ID must be specified")
|
||||
case 1:
|
||||
src = args[0]
|
||||
destSpec = src
|
||||
logger.Debug("Destination argument not specified, assuming the same as the source: %s", destSpec)
|
||||
case 2:
|
||||
src = args[0]
|
||||
destSpec = args[1]
|
||||
if src == "" {
|
||||
return fmt.Errorf(`invalid image name "%s"`, args[0])
|
||||
}
|
||||
default:
|
||||
return errors.New("only two arguments are necessary to push: source and destination")
|
||||
}
|
||||
|
||||
compress := define.Gzip
|
||||
if iopts.disableCompression {
|
||||
compress = define.Uncompressed
|
||||
}
|
||||
|
||||
store, err := getStore(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dest, err := alltransports.ParseImageName(destSpec)
|
||||
// add the docker:// transport to see if they neglected it.
|
||||
if err != nil {
|
||||
destTransport := strings.Split(destSpec, ":")[0]
|
||||
if t := transports.Get(destTransport); t != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if strings.Contains(destSpec, "://") {
|
||||
return err
|
||||
}
|
||||
|
||||
destSpec = "docker://" + destSpec
|
||||
dest2, err2 := alltransports.ParseImageName(destSpec)
|
||||
if err2 != nil {
|
||||
return err
|
||||
}
|
||||
dest = dest2
|
||||
logger.Debug("Assuming docker:// as the transport method for DESTINATION: %s", destSpec)
|
||||
}
|
||||
|
||||
systemContext, err := parse.SystemContextFromOptions(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("building system context: %w", err)
|
||||
}
|
||||
|
||||
var manifestType string
|
||||
if iopts.format != "" {
|
||||
switch iopts.format {
|
||||
case "oci":
|
||||
manifestType = imgspecv1.MediaTypeImageManifest
|
||||
case "v2s1":
|
||||
manifestType = manifest.DockerV2Schema1SignedMediaType
|
||||
case "v2s2", "docker":
|
||||
manifestType = manifest.DockerV2Schema2MediaType
|
||||
default:
|
||||
return fmt.Errorf("unknown format %q. Choose on of the supported formats: 'oci', 'v2s1', or 'v2s2'", iopts.format)
|
||||
}
|
||||
}
|
||||
|
||||
encConfig, encLayers, err := iutil.EncryptConfig(iopts.encryptionKeys, iopts.encryptLayers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to obtain encryption config: %w", err)
|
||||
}
|
||||
|
||||
options := buildah.PushOptions{
|
||||
Compression: compress,
|
||||
ManifestType: manifestType,
|
||||
SignaturePolicyPath: iopts.signaturePolicy,
|
||||
Store: store,
|
||||
SystemContext: systemContext,
|
||||
BlobDirectory: iopts.blobCache,
|
||||
RemoveSignatures: iopts.removeSignatures,
|
||||
SignBy: iopts.signBy,
|
||||
MaxRetries: iopts.retry,
|
||||
RetryDelay: iopts.retryDelay,
|
||||
OciEncryptConfig: encConfig,
|
||||
OciEncryptLayers: encLayers,
|
||||
}
|
||||
if !iopts.quiet {
|
||||
options.ReportWriter = os.Stderr
|
||||
}
|
||||
if iopts.compressionFormat != "" {
|
||||
algo, err := compression.AlgorithmByName(iopts.compressionFormat)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options.CompressionFormat = &algo
|
||||
}
|
||||
if flagChanged(c, "compression-level") {
|
||||
options.CompressionLevel = &iopts.compressionLevel
|
||||
}
|
||||
|
||||
ref, digest, err := buildah.Push(getContext(), src, dest, options)
|
||||
if err != nil {
|
||||
if !errors.Is(err, storage.ErrImageUnknown) {
|
||||
// Image might be a manifest so attempt a manifest push
|
||||
if manifestsErr := manifestPush(systemContext, store, src, destSpec, *iopts); manifestsErr == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return util.GetFailureCause(err, fmt.Errorf("pushing image %q to %q: %w", src, destSpec, err))
|
||||
}
|
||||
if ref != nil {
|
||||
logger.Debug("pushed image %q with digest %s", ref, digest.String())
|
||||
} else {
|
||||
logger.Debug("pushed image with digest %s", digest.String())
|
||||
}
|
||||
|
||||
logger.Debug("Successfully pushed %s with digest %s", transports.ImageName(dest), digest.String())
|
||||
|
||||
if iopts.digestfile != "" {
|
||||
if err = os.WriteFile(iopts.digestfile, []byte(digest.String()), 0644); err != nil {
|
||||
return util.GetFailureCause(err, fmt.Errorf("failed to write digest to file %q: %w", iopts.digestfile, err))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getListOfTransports gets the transports supported from the image library
|
||||
// and strips of the "tarball" transport from the string of transports returned
|
||||
func getListOfTransports() string {
|
||||
allTransports := strings.Join(transports.ListNames(), ",")
|
||||
return strings.Replace(allTransports, ",tarball", "", 1)
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
// Copyright © 2022 buildah.
|
||||
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 buildah
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
buildahcli "github.com/containers/buildah/pkg/cli"
|
||||
"github.com/containers/buildah/util"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type rmResults struct {
|
||||
all bool
|
||||
}
|
||||
|
||||
func newRMCommand() *cobra.Command {
|
||||
var (
|
||||
rmDescription = "\n Removes one or more working containers, unmounting them if necessary."
|
||||
opts rmResults
|
||||
)
|
||||
rmCommand := &cobra.Command{
|
||||
Use: "rm",
|
||||
Hidden: true,
|
||||
Short: "Remove one or more working containers",
|
||||
Long: rmDescription,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return rmCmd(cmd, args, opts)
|
||||
},
|
||||
Example: fmt.Sprintf(`%[1]s rm containerID
|
||||
%[1]s rm containerID1 containerID2 containerID3
|
||||
%[1]s rm --all`, rootCmd.Name()),
|
||||
}
|
||||
rmCommand.SetUsageTemplate(UsageTemplate())
|
||||
|
||||
flags := rmCommand.Flags()
|
||||
flags.SetInterspersed(false)
|
||||
flags.BoolVarP(&opts.all, "all", "a", false, "remove all containers")
|
||||
return rmCommand
|
||||
}
|
||||
|
||||
func rmCmd(c *cobra.Command, args []string, iopts rmResults) error {
|
||||
delContainerErrStr := "removing container"
|
||||
if len(args) == 0 && !iopts.all {
|
||||
return errors.New("container ID must be specified")
|
||||
}
|
||||
if len(args) > 0 && iopts.all {
|
||||
return errors.New("when using the --all switch, you may not pass any containers names or IDs")
|
||||
}
|
||||
|
||||
if err := buildahcli.VerifyFlagsArgsOrder(args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
store, err := getStore(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var lastError error
|
||||
if iopts.all {
|
||||
builders, err := openBuilders(store)
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading build containers: %w", err)
|
||||
}
|
||||
|
||||
for _, builder := range builders {
|
||||
id := builder.ContainerID
|
||||
if err = builder.Delete(); err != nil {
|
||||
lastError = util.WriteError(os.Stderr, fmt.Errorf("%s %q: %w", delContainerErrStr, builder.Container, err), lastError)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("%s\n", id)
|
||||
}
|
||||
} else {
|
||||
for _, name := range args {
|
||||
builder, err := openBuilder(getContext(), store, name)
|
||||
if err != nil {
|
||||
lastError = util.WriteError(os.Stderr, fmt.Errorf("%s %q: %w", delContainerErrStr, name, err), lastError)
|
||||
continue
|
||||
}
|
||||
id := builder.ContainerID
|
||||
if err = builder.Delete(); err != nil {
|
||||
lastError = util.WriteError(os.Stderr, fmt.Errorf("%s %q: %w", delContainerErrStr, name, err), lastError)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("%s\n", id)
|
||||
}
|
||||
}
|
||||
return lastError
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
// Copyright © 2022 buildah.
|
||||
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 buildah
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
buildahcli "github.com/containers/buildah/pkg/cli"
|
||||
"github.com/containers/buildah/pkg/parse"
|
||||
"github.com/containers/common/libimage"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type rmiOptions struct {
|
||||
all bool
|
||||
prune bool
|
||||
force bool
|
||||
}
|
||||
|
||||
func newRMICommand() *cobra.Command {
|
||||
var (
|
||||
rmiDescription = "\n Removes one or more locally stored images."
|
||||
opts rmiOptions
|
||||
)
|
||||
rmiCommand := &cobra.Command{
|
||||
Use: "rmi",
|
||||
Aliases: []string{"prune"},
|
||||
Short: "Remove one or more images from local storage",
|
||||
Long: rmiDescription,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return rmiCmd(cmd, args, opts)
|
||||
},
|
||||
Example: fmt.Sprintf(`%[1]s rmi imageID
|
||||
%[1]s rmi --all --force
|
||||
%[1]s rmi imageID1 imageID2 imageID3`, rootCmd.Name()),
|
||||
}
|
||||
rmiCommand.SetUsageTemplate(UsageTemplate())
|
||||
|
||||
flags := rmiCommand.Flags()
|
||||
flags.SetInterspersed(false)
|
||||
flags.BoolVarP(&opts.all, "all", "a", false, "remove all images")
|
||||
flags.BoolVarP(&opts.prune, "prune", "p", false, "prune dangling images")
|
||||
flags.BoolVarP(&opts.force, "force", "f", false, "force removal of the image and any containers using the image")
|
||||
|
||||
return rmiCommand
|
||||
}
|
||||
|
||||
func rmiCmd(c *cobra.Command, args []string, iopts rmiOptions) error {
|
||||
if len(args) == 0 && !iopts.all && !iopts.prune {
|
||||
return errors.New("image name or ID must be specified")
|
||||
}
|
||||
if len(args) > 0 && iopts.all {
|
||||
return errors.New("when using the --all switch, you may not pass any images names or IDs")
|
||||
}
|
||||
if iopts.all && iopts.prune {
|
||||
return errors.New("when using the --all switch, you may not use --prune switch")
|
||||
}
|
||||
if len(args) > 0 && iopts.prune {
|
||||
return errors.New("when using the --prune switch, you may not pass any images names or IDs")
|
||||
}
|
||||
|
||||
if err := buildahcli.VerifyFlagsArgsOrder(args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
store, err := getStore(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
systemContext, err := parse.SystemContextFromOptions(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
runtime, err := libimage.RuntimeFromStore(store, &libimage.RuntimeOptions{SystemContext: systemContext})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
options := &libimage.RemoveImagesOptions{
|
||||
Filters: []string{"readonly=false"},
|
||||
}
|
||||
if iopts.prune {
|
||||
options.Filters = append(options.Filters, "dangling=true")
|
||||
} else if !iopts.all {
|
||||
options.Filters = append(options.Filters, "intermediate=false")
|
||||
}
|
||||
options.Force = iopts.force
|
||||
|
||||
rmiReports, rmiErrors := runtime.RemoveImages(context.Background(), args, options)
|
||||
for _, r := range rmiReports {
|
||||
for _, u := range r.Untagged {
|
||||
fmt.Printf("untagged: %s\n", u)
|
||||
}
|
||||
}
|
||||
for _, r := range rmiReports {
|
||||
if r.Removed {
|
||||
fmt.Printf("%s\n", r.ID)
|
||||
}
|
||||
}
|
||||
|
||||
var multiE *multierror.Error
|
||||
multiE = multierror.Append(multiE, rmiErrors...)
|
||||
return multiE.ErrorOrNil()
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright © 2022 buildah.
|
||||
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 buildah
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newSaveCommand() *cobra.Command {
|
||||
var (
|
||||
opts = newDefaultPushOptions()
|
||||
archiveName string
|
||||
)
|
||||
|
||||
saveCommand := &cobra.Command{
|
||||
Use: "save",
|
||||
Short: "Save image into archive file",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return pushCmd(cmd, []string{
|
||||
args[0],
|
||||
fmt.Sprintf("%s:%s:%s", DockerArchive, archiveName, args[0]),
|
||||
}, opts)
|
||||
},
|
||||
Example: fmt.Sprintf(`%[1]s save -o kubernetes.tar labring/kubernetes:latest`, rootCmd.Name()),
|
||||
}
|
||||
saveCommand.SetUsageTemplate(UsageTemplate())
|
||||
|
||||
saveCommand.Flags().StringVarP(&archiveName, "output", "o", "", "save image into tar archive file")
|
||||
return saveCommand
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
// Copyright © 2022 sealos.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package buildah
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/common/pkg/config"
|
||||
"github.com/containers/storage/pkg/homedir"
|
||||
"github.com/containers/storage/pkg/unshare"
|
||||
"github.com/containers/storage/types"
|
||||
|
||||
"github.com/labring/sealos/pkg/utils/file"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
)
|
||||
|
||||
var (
|
||||
DefaultConfigFile string
|
||||
DefaultSignaturePolicyPath = config.DefaultSignaturePolicyPath
|
||||
DefaultRootlessSignaturePolicyPath = "containers/policy.json"
|
||||
DefaultGraphRoot = "/var/lib/containers/storage"
|
||||
DefaultRegistriesFilePath = "/etc/containers/registries.conf"
|
||||
DefaultRootlessRegistriesFilePath = "containers/registries.conf"
|
||||
)
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
DefaultConfigFile, err = types.DefaultConfigFile(IsRootless())
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
if IsRootless() {
|
||||
configHome, err := homedir.GetConfigHome()
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
DefaultSignaturePolicyPath = filepath.Join(configHome, DefaultRootlessSignaturePolicyPath)
|
||||
DefaultRegistriesFilePath = filepath.Join(configHome, DefaultRootlessRegistriesFilePath)
|
||||
}
|
||||
}
|
||||
|
||||
const defaultPolicy = `
|
||||
{
|
||||
"default": [
|
||||
{
|
||||
"type": "insecureAcceptAnything"
|
||||
}
|
||||
],
|
||||
"transports":
|
||||
{
|
||||
"docker-daemon":
|
||||
{
|
||||
"": [{"type":"insecureAcceptAnything"}]
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const defaultRegistries = `unqualified-search-registries = ["docker.io"]
|
||||
|
||||
[[registry]]
|
||||
prefix = "docker.io/labring"
|
||||
location = "docker.io/labring"
|
||||
`
|
||||
|
||||
const defaultStorageConf = `[storage]
|
||||
driver = "overlay"
|
||||
runroot = "/run/containers/storage"
|
||||
graphroot = "/var/lib/containers/storage"`
|
||||
|
||||
func SetupContainerPolicy() error {
|
||||
return writeFileIfNotExists(DefaultSignaturePolicyPath, []byte(defaultPolicy))
|
||||
}
|
||||
|
||||
func SetupRegistriesFile() error {
|
||||
return writeFileIfNotExists(DefaultRegistriesFilePath, []byte(defaultRegistries))
|
||||
}
|
||||
|
||||
func SetupStorageConfigFile() error {
|
||||
if IsRootless() {
|
||||
return nil
|
||||
}
|
||||
return writeFileIfNotExists(DefaultConfigFile, []byte(defaultStorageConf))
|
||||
}
|
||||
|
||||
func writeFileIfNotExists(filename string, data []byte) error {
|
||||
_, err := os.Stat(filename)
|
||||
if os.IsNotExist(err) {
|
||||
logger.Debug("create new buildah config %s cause it's not exist", filename)
|
||||
err = file.WriteFile(filename, data)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func IsRootless() bool {
|
||||
if v, ok := os.LookupEnv(DisableAutoRootless); ok && strings.ToLower(v) == "true" {
|
||||
return false
|
||||
}
|
||||
return unshare.IsRootless()
|
||||
}
|
||||
|
||||
func MaybeReexecUsingUserNamespace() error {
|
||||
if !IsRootless() {
|
||||
return nil
|
||||
}
|
||||
if _, present := os.LookupEnv("BUILDAH_ISOLATION"); !present {
|
||||
if err := os.Setenv("BUILDAH_ISOLATION", "rootless"); err != nil {
|
||||
return fmt.Errorf("error setting BUILDAH_ISOLATION=rootless in environment: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// force reexec using the configured ID mappings
|
||||
unshare.MaybeReexecUsingUserNamespace(true)
|
||||
return nil
|
||||
}
|
||||
|
||||
type Setter func() error
|
||||
|
||||
var defaultSetters = []Setter{
|
||||
MaybeReexecUsingUserNamespace,
|
||||
SetupContainerPolicy,
|
||||
SetupRegistriesFile,
|
||||
SetupStorageConfigFile,
|
||||
}
|
||||
|
||||
func TrySetupWithDefaults(setters ...Setter) error {
|
||||
if len(setters) == 0 {
|
||||
setters = defaultSetters
|
||||
}
|
||||
for i := range setters {
|
||||
if err := setters[i](); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// Copyright © 2022 buildah.
|
||||
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 buildah
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/containers/buildah/pkg/parse"
|
||||
"github.com/containers/common/libimage"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newTagCommand() *cobra.Command {
|
||||
var (
|
||||
tagDescription = "\n Adds one or more additional names to locally-stored image."
|
||||
)
|
||||
tagCommand := &cobra.Command{
|
||||
Use: "tag",
|
||||
Short: "Add an additional name to a local image",
|
||||
Long: tagDescription,
|
||||
RunE: tagCmd,
|
||||
|
||||
Example: fmt.Sprintf(`%[1]s tag imageName firstNewName
|
||||
%[1]s tag imageName firstNewName SecondNewName`, rootCmd.Name()),
|
||||
Args: cobra.MinimumNArgs(2),
|
||||
}
|
||||
tagCommand.SetUsageTemplate(UsageTemplate())
|
||||
return tagCommand
|
||||
}
|
||||
|
||||
func tagCmd(c *cobra.Command, args []string) error {
|
||||
store, err := getStore(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
systemContext, err := parse.SystemContextFromOptions(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("building system context: %w", err)
|
||||
}
|
||||
runtime, err := libimage.RuntimeFromStore(store, &libimage.RuntimeOptions{SystemContext: systemContext})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Allow tagging manifest list instead of resolving instances from manifest
|
||||
lookupOptions := &libimage.LookupImageOptions{ManifestList: true}
|
||||
image, _, err := runtime.LookupImage(args[0], lookupOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, tag := range args[1:] {
|
||||
if err := image.Tag(tag); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
// Copyright © 2022 buildah.
|
||||
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 buildah
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
buildahcli "github.com/containers/buildah/pkg/cli"
|
||||
"github.com/containers/storage"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newUmountCommand() *cobra.Command {
|
||||
umountCommand := &cobra.Command{
|
||||
Use: "umount",
|
||||
Aliases: []string{"unmount"},
|
||||
Hidden: true,
|
||||
Short: "Unmount the root file system of the specified working containers",
|
||||
Long: "Unmounts the root file system of the specified working containers.",
|
||||
RunE: umountCmd,
|
||||
Example: fmt.Sprintf(`%[1]s umount containerID
|
||||
%[1]s umount containerID1 containerID2 containerID3
|
||||
%[1]s umount --all`, rootCmd.Name()),
|
||||
}
|
||||
umountCommand.SetUsageTemplate(UsageTemplate())
|
||||
|
||||
flags := umountCommand.Flags()
|
||||
flags.SetInterspersed(false)
|
||||
flags.BoolP("all", "a", false, "umount all of the currently mounted containers")
|
||||
return umountCommand
|
||||
}
|
||||
|
||||
func umountCmd(c *cobra.Command, args []string) error {
|
||||
umountAll := false
|
||||
if flagChanged(c, "all") {
|
||||
umountAll = true
|
||||
}
|
||||
if len(args) == 0 && !umountAll {
|
||||
return errors.New("at least one container ID must be specified")
|
||||
}
|
||||
if len(args) > 0 && umountAll {
|
||||
return errors.New("when using the --all switch, you may not pass any container IDs")
|
||||
}
|
||||
if err := buildahcli.VerifyFlagsArgsOrder(args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
store, err := getStore(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = doUMounts(store, args)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func doUMounts(store storage.Store, args []string) ([]string, error) {
|
||||
umountContainerErrStr := "error unmounting container"
|
||||
var ids []string
|
||||
if len(args) > 0 {
|
||||
for _, name := range args {
|
||||
builder, err := openBuilder(getContext(), store, name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s %s: %w", umountContainerErrStr, name, err)
|
||||
}
|
||||
if builder.MountPoint == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if err = builder.Unmount(); err != nil {
|
||||
return nil, fmt.Errorf("%s %q: %w", umountContainerErrStr, builder.Container, err)
|
||||
}
|
||||
ids = append(ids, builder.ContainerID)
|
||||
}
|
||||
} else {
|
||||
builders, err := openBuilders(store)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading build Containers: %w", err)
|
||||
}
|
||||
for _, builder := range builders {
|
||||
if builder.MountPoint == "" {
|
||||
continue
|
||||
}
|
||||
if err = builder.Unmount(); err != nil {
|
||||
return nil, fmt.Errorf("%s %q: %w", umountContainerErrStr, builder.Container, err)
|
||||
}
|
||||
ids = append(ids, builder.ContainerID)
|
||||
}
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
@@ -61,10 +61,10 @@ func checkHostnameUnique(s ssh.Interface, ipList []string) error {
|
||||
for _, ip := range ipList {
|
||||
hostname, err := s.CmdToString(ip, "hostname", "")
|
||||
if err != nil {
|
||||
return fmt.Errorf("checker: failed to get host %s hostname, %v", ip, err)
|
||||
return fmt.Errorf("failed to get host %s hostname, %v", ip, err)
|
||||
}
|
||||
if hostnameList[hostname] {
|
||||
return fmt.Errorf("checker: hostname cannot be repeated, please set different hostname")
|
||||
return fmt.Errorf("hostname cannot be repeated, please set different hostname")
|
||||
}
|
||||
hostnameList[hostname] = true
|
||||
}
|
||||
@@ -77,15 +77,15 @@ func checkTimeSync(s ssh.Interface, ipList []string) error {
|
||||
for _, ip := range ipList {
|
||||
timeStamp, err := s.CmdToString(ip, "date +%s", "")
|
||||
if err != nil {
|
||||
return fmt.Errorf("checker: failed to get %s timestamp, %v", ip, err)
|
||||
return fmt.Errorf("failed to get %s timestamp, %v", ip, err)
|
||||
}
|
||||
ts, err := strconv.Atoi(timeStamp)
|
||||
if err != nil {
|
||||
return fmt.Errorf("checker: failed to reverse timestamp %s, %v", timeStamp, err)
|
||||
return fmt.Errorf("failed to reverse timestamp %s, %v", timeStamp, err)
|
||||
}
|
||||
timeDiff := time.Since(time.Unix(int64(ts), 0)).Minutes()
|
||||
if timeDiff < -1 || timeDiff > 1 {
|
||||
return fmt.Errorf("checker: the time of %s node is not synchronized", ip)
|
||||
return fmt.Errorf("the time of %s node is not synchronized", ip)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -58,6 +58,7 @@ func (c *ClusterFile) Process() (err error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Debug("rendered Clusterfile: %+v", string(clusterFileData))
|
||||
return c.decode(clusterFileData)
|
||||
}()
|
||||
})
|
||||
@@ -77,7 +78,6 @@ func (c *ClusterFile) loadClusterFile() ([]byte, error) {
|
||||
data := map[string]interface{}{
|
||||
"Values": mergeValues,
|
||||
}
|
||||
logger.Debug("loadClusterFile body: %+v", string(body))
|
||||
out := bytes.NewBuffer(nil)
|
||||
tpl, isOk, err := template.TryParse(string(body))
|
||||
if isOk {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
package constants
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/containers/storage/pkg/homedir"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -40,11 +40,7 @@ const (
|
||||
)
|
||||
|
||||
func GetHomeDir() string {
|
||||
home, err := homedir.Dir()
|
||||
if err != nil {
|
||||
return "/root"
|
||||
}
|
||||
return home
|
||||
return homedir.Get()
|
||||
}
|
||||
|
||||
var Contact = `
|
||||
|
||||
+28
-6
@@ -17,12 +17,24 @@ limitations under the License.
|
||||
package constants
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/storage/pkg/homedir"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultDataRoot = "/var/lib"
|
||||
AppName = "sealos"
|
||||
)
|
||||
|
||||
var (
|
||||
DefaultClusterRootfsDir = "/var/lib/sealos"
|
||||
DefaultClusterRootFsDir = path.Join(defaultDataRoot, AppName)
|
||||
DefaultRuntimeRootDir = GetRuntimeRootDir(AppName)
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -44,11 +56,19 @@ const (
|
||||
StaticsDirName = "statics"
|
||||
)
|
||||
|
||||
func LogPath() string {
|
||||
return filepath.Join(DefaultClusterRootfsDir, "logs")
|
||||
func GetRuntimeRootDir(name string) string {
|
||||
if v, ok := os.LookupEnv(strings.ToUpper(name) + "_RUNTIME_ROOT"); ok {
|
||||
return v
|
||||
}
|
||||
return path.Join(homedir.Get(), fmt.Sprintf(".%s", name))
|
||||
}
|
||||
|
||||
func LogPath() string {
|
||||
return filepath.Join(DefaultRuntimeRootDir, "logs")
|
||||
}
|
||||
|
||||
func DataPath() string {
|
||||
return filepath.Join(DefaultClusterRootfsDir, "data")
|
||||
return filepath.Join(DefaultClusterRootFsDir, "data")
|
||||
}
|
||||
|
||||
func GetAppWorkDir(clusterName, applicationName string) string {
|
||||
@@ -96,6 +116,7 @@ func (d *data) RootFSEtcPath() string {
|
||||
func (d *data) RootFSRegistryPath() string {
|
||||
return filepath.Join(d.RootFSPath(), RegistryDirName)
|
||||
}
|
||||
|
||||
func (d *data) RootFSCharsPath() string {
|
||||
return filepath.Join(d.RootFSPath(), ChartsDirName)
|
||||
}
|
||||
@@ -107,6 +128,7 @@ func (d *data) RootFSManifestsPath() string {
|
||||
func (d *data) EtcPath() string {
|
||||
return filepath.Join(ClusterDir(d.clusterName), EtcDirName)
|
||||
}
|
||||
|
||||
func (d *data) AdminFile() string {
|
||||
return filepath.Join(d.EtcPath(), "admin.conf")
|
||||
}
|
||||
@@ -120,7 +142,7 @@ func (d *data) PkiEtcdPath() string {
|
||||
}
|
||||
|
||||
func (d *data) TmpPath() string {
|
||||
return filepath.Join(d.Homedir(), "tmp")
|
||||
return filepath.Join(ClusterDir(d.clusterName), "tmp")
|
||||
}
|
||||
|
||||
func (d *data) RootFSPath() string {
|
||||
@@ -132,7 +154,7 @@ func (d *data) RootFSStaticsPath() string {
|
||||
}
|
||||
|
||||
func (d *data) Homedir() string {
|
||||
return filepath.Join(DefaultClusterRootfsDir, "data", d.clusterName)
|
||||
return filepath.Join(DefaultClusterRootFsDir, "data", d.clusterName)
|
||||
}
|
||||
|
||||
func NewData(clusterName string) Data {
|
||||
|
||||
@@ -23,7 +23,7 @@ const (
|
||||
)
|
||||
|
||||
func Workdir() string {
|
||||
return filepath.Join(GetHomeDir(), ".sealos")
|
||||
return DefaultRuntimeRootDir
|
||||
}
|
||||
|
||||
func ClusterDir(clusterName string) string {
|
||||
|
||||
@@ -66,16 +66,16 @@ func (f *defaultRootfs) mountRootfs(cluster *v2.Cluster, ipList []string) error
|
||||
src := mount
|
||||
eg.Go(func() error {
|
||||
if !file.IsExist(src.MountPoint) {
|
||||
logger.Debug("Image %s not exist,render env continue", src.ImageName)
|
||||
logger.Debug("Image %s not exist, render env continue", src.ImageName)
|
||||
return nil
|
||||
}
|
||||
err := renderENV(src.MountPoint, ipList, envProcessor)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "render env to rootfs failed")
|
||||
return errors.Wrap(err, "failed to render env")
|
||||
}
|
||||
dirs, err := file.StatDir(src.MountPoint, true)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "get rootfs files failed")
|
||||
return errors.Wrap(err, "failed to stat files")
|
||||
}
|
||||
if len(dirs) != 0 {
|
||||
_, err = exec.RunBashCmd(fmt.Sprintf(constants.DefaultChmodBash, src.MountPoint))
|
||||
|
||||
+8
-19
@@ -19,23 +19,17 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/labring/sealos/fork/golang/expansion"
|
||||
"github.com/labring/sealos/pkg/constants"
|
||||
|
||||
"github.com/labring/sealos/pkg/env"
|
||||
"github.com/labring/sealos/pkg/runtime"
|
||||
"github.com/labring/sealos/pkg/ssh"
|
||||
|
||||
v2 "github.com/labring/sealos/pkg/types/v1beta1"
|
||||
fileutil "github.com/labring/sealos/pkg/utils/file"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
"github.com/labring/sealos/pkg/utils/maps"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/client-go/util/homedir"
|
||||
|
||||
"github.com/labring/sealos/fork/golang/expansion"
|
||||
"github.com/labring/sealos/pkg/env"
|
||||
"github.com/labring/sealos/pkg/image"
|
||||
"github.com/labring/sealos/pkg/image/types"
|
||||
"github.com/labring/sealos/pkg/runtime"
|
||||
v2 "github.com/labring/sealos/pkg/types/v1beta1"
|
||||
)
|
||||
|
||||
type Interface interface {
|
||||
@@ -44,15 +38,10 @@ type Interface interface {
|
||||
}
|
||||
|
||||
type Default struct {
|
||||
imageService types.ImageService
|
||||
}
|
||||
|
||||
func NewGuestManager() (Interface, error) {
|
||||
is, err := image.NewImageService()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Default{imageService: is}, nil
|
||||
return &Default{}, nil
|
||||
}
|
||||
|
||||
func (d *Default) Apply(cluster *v2.Cluster, mounts []v2.MountImage) error {
|
||||
@@ -60,7 +49,7 @@ func (d *Default) Apply(cluster *v2.Cluster, mounts []v2.MountImage) error {
|
||||
envs := envInterface.WrapperEnv(cluster.GetMaster0IP()) //clusterfile
|
||||
guestCMD := d.getGuestCmd(envs, cluster, mounts)
|
||||
|
||||
kubeConfig := filepath.Join(homedir.HomeDir(), ".kube", "config")
|
||||
kubeConfig := filepath.Join(constants.GetHomeDir(), ".kube", "config")
|
||||
if !fileutil.IsExist(kubeConfig) {
|
||||
adminFile := runtime.GetConstantData(cluster.Name).AdminFile()
|
||||
data, err := fileutil.ReadAll(adminFile)
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
|
||||
"github.com/labring/sealos/pkg/constants"
|
||||
|
||||
"github.com/labring/sealos/pkg/image/types"
|
||||
v2 "github.com/labring/sealos/pkg/types/v1beta1"
|
||||
)
|
||||
|
||||
@@ -32,7 +31,6 @@ func TestDefault_getGuestCmd(t *testing.T) {
|
||||
return fmt.Sprintf(constants.CdAndExecCmd, constants.GetAppWorkDir(cName, containerName), cmd)
|
||||
}
|
||||
type fields struct {
|
||||
imageService types.ImageService
|
||||
}
|
||||
type args struct {
|
||||
envs map[string]string
|
||||
@@ -129,9 +127,7 @@ func TestDefault_getGuestCmd(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
d := &Default{
|
||||
imageService: tt.fields.imageService,
|
||||
}
|
||||
d := &Default{}
|
||||
if got := d.getGuestCmd(tt.args.envs, tt.args.cluster, tt.args.mounts); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("getGuestCmd() = %v, want %v", got, tt.want)
|
||||
}
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
/*
|
||||
Copyright 2022 cuisongliu@qq.com.
|
||||
|
||||
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 binary
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/labring/sealos/pkg/utils/exec"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/util/json"
|
||||
|
||||
"github.com/labring/sealos/pkg/image/types"
|
||||
)
|
||||
|
||||
type ClusterService struct {
|
||||
}
|
||||
|
||||
func (s *ClusterService) Create(name string, image string) (*types.ClusterManifest, error) {
|
||||
if err := s.Delete(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmd := fmt.Sprintf("buildah from --pull=never --name %s %s && buildah mount %s ", name, image, name)
|
||||
if err := exec.Cmd("bash", "-c", cmd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.Inspect(name)
|
||||
}
|
||||
|
||||
func (s *ClusterService) Delete(name string) error {
|
||||
infos, err := s.List()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Debug("current container names is: %v", name)
|
||||
for _, info := range infos {
|
||||
if info.Containername == name {
|
||||
cmd := fmt.Sprintf("buildah unmount %s && buildah rm %s", info.Containername, info.Containername)
|
||||
if err = exec.Cmd("bash", "-c", cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*ClusterService) Inspect(name string) (*types.ClusterManifest, error) {
|
||||
data := exec.BashEval(fmt.Sprintf("buildah inspect %s", name))
|
||||
manifest, err := inspectContainer(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return manifest, nil
|
||||
}
|
||||
|
||||
func (*ClusterService) List() ([]types.ClusterInfo, error) {
|
||||
data := exec.BashEval("buildah containers --json")
|
||||
return listContainer(data)
|
||||
}
|
||||
|
||||
func inspectContainer(data string) (*types.ClusterManifest, error) {
|
||||
if data != "" {
|
||||
var outStruct map[string]interface{}
|
||||
err := json.Unmarshal([]byte(data), &outStruct)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "decode out json from container inspect failed")
|
||||
}
|
||||
container, _, _ := unstructured.NestedString(outStruct, "Container")
|
||||
containerID, _, _ := unstructured.NestedString(outStruct, "ContainerID")
|
||||
mountPoint, _, _ := unstructured.NestedString(outStruct, "MountPoint")
|
||||
manifest := &types.ClusterManifest{
|
||||
Container: container,
|
||||
ContainerID: containerID,
|
||||
MountPoint: mountPoint,
|
||||
}
|
||||
return manifest, nil
|
||||
}
|
||||
return nil, errors.New("inspect output is empty")
|
||||
}
|
||||
|
||||
func listContainer(data string) ([]types.ClusterInfo, error) {
|
||||
if data != "" {
|
||||
var outStruct []types.ClusterInfo
|
||||
err := json.Unmarshal([]byte(data), &outStruct)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "decode out json from list container failed")
|
||||
}
|
||||
return outStruct, nil
|
||||
}
|
||||
return nil, errors.New("containers output is empty")
|
||||
}
|
||||
|
||||
func NewClusterService() (types.ClusterService, error) {
|
||||
return &ClusterService{}, nil
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
/*
|
||||
Copyright 2022 cuisongliu@qq.com.
|
||||
|
||||
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 binary
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/labring/sealos/pkg/constants"
|
||||
"github.com/labring/sealos/pkg/utils/exec"
|
||||
fileutil "github.com/labring/sealos/pkg/utils/file"
|
||||
json2 "github.com/labring/sealos/pkg/utils/json"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/util/json"
|
||||
|
||||
"github.com/labring/sealos/pkg/buildimage"
|
||||
"github.com/labring/sealos/pkg/image/types"
|
||||
"github.com/labring/sealos/pkg/registry"
|
||||
)
|
||||
|
||||
// ImageService is the default service, which is used for image pull/push
|
||||
type ImageService struct {
|
||||
}
|
||||
|
||||
func (d *ImageService) Tag(src, dst string) error {
|
||||
return exec.Cmd("bash", "-c", fmt.Sprintf("buildah tag %s %s", src, dst))
|
||||
}
|
||||
|
||||
func (d *ImageService) Save(imageName, archiveName string) error {
|
||||
localDir := filepath.Dir(archiveName)
|
||||
if !fileutil.IsExist(localDir) {
|
||||
return errors.New("archive dir is not exist")
|
||||
}
|
||||
return exec.Cmd("bash", "-c", fmt.Sprintf("buildah push %s %s:%s:%s", imageName, types.DefaultTransport, archiveName, imageName))
|
||||
}
|
||||
|
||||
func (d *ImageService) Load(archiveName string) (string, error) {
|
||||
if !fileutil.IsExist(archiveName) {
|
||||
return "", errors.New("archive file is not exist")
|
||||
}
|
||||
output, err := exec.Output("bash", "-c", fmt.Sprintf("buildah pull %s:%s", types.DefaultTransport, archiveName))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
l := len(output)
|
||||
id := string(output[l-65 : l-1])
|
||||
logger.Info("load image %s", id)
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (d *ImageService) Remove(force bool, images ...string) error {
|
||||
var forceCMD string
|
||||
if force {
|
||||
forceCMD = "-f"
|
||||
}
|
||||
cmd := fmt.Sprintf("buildah rmi %s %s", forceCMD, strings.Join(images, " "))
|
||||
return exec.Cmd("bash", "-c", cmd)
|
||||
}
|
||||
|
||||
func (d *ImageService) Inspect(images ...string) (types.ImageListOCIV1, error) {
|
||||
var imageList types.ImageListOCIV1
|
||||
for _, image := range images {
|
||||
data := exec.BashEval(fmt.Sprintf("buildah inspect %s", image))
|
||||
ociImage, err := inspectImage(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
imageList = append(imageList, *ociImage)
|
||||
}
|
||||
|
||||
return imageList, nil
|
||||
}
|
||||
|
||||
func inspectImage(data string) (*v1.Image, error) {
|
||||
if data != "" {
|
||||
var outStruct map[string]interface{}
|
||||
err := json.Unmarshal([]byte(data), &outStruct)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "decode out json from image inspect failed")
|
||||
}
|
||||
imageData, _, err := unstructured.NestedFieldCopy(outStruct, "OCIv1")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "decode out json from OCIv1 object failed")
|
||||
}
|
||||
img := &v1.Image{}
|
||||
err = json2.Convert(imageData, img)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "decode OCIv1 to v1.Image failed")
|
||||
}
|
||||
return img, nil
|
||||
}
|
||||
return nil, errors.New("inspect output is empty")
|
||||
}
|
||||
|
||||
func (d *ImageService) Build(options *types.BuildOptions, contextDir, imageName string) error {
|
||||
images, err := buildimage.List(contextDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
auths, err := registry.GetAuthInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
is := registry.NewImageSaver(context.Background(), options.MaxPullProcs, auths)
|
||||
platformVar := types.ParsePlatform(options.Platform)
|
||||
logger.Info("pull images %v for platform is %s", images, strings.Join([]string{platformVar.OS, platformVar.Architecture}, "/"))
|
||||
if options.SaveImage {
|
||||
images, err = is.SaveImages(images, path.Join(contextDir, constants.RegistryDirName), platformVar)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "save images failed in this context")
|
||||
}
|
||||
logger.Info("output images %v for platform is %s", images, strings.Join([]string{platformVar.OS, platformVar.Architecture}, "/"))
|
||||
} else {
|
||||
logger.Warn("current saveImage=false, skip pull images")
|
||||
}
|
||||
options.Tag = imageName
|
||||
cmd := fmt.Sprintf("buildah build --tls-verify=false %s %s", options.String(), contextDir)
|
||||
return exec.Cmd("bash", "-c", cmd)
|
||||
}
|
||||
|
||||
func (d *ImageService) Prune() error {
|
||||
return exec.Cmd("bash", "-c", "buildah rmi --prune")
|
||||
}
|
||||
|
||||
func (d *ImageService) ListImages() error {
|
||||
data, err := exec.RunBashCmd("buildah images")
|
||||
_, _ = os.Stdout.Write([]byte(data))
|
||||
return err
|
||||
}
|
||||
|
||||
func NewImageService() (types.ImageService, error) {
|
||||
return &ImageService{}, nil
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
Copyright 2022 cuisongliu@qq.com.
|
||||
|
||||
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 binary
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
|
||||
"github.com/labring/sealos/pkg/utils/exec"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
|
||||
"github.com/labring/sealos/pkg/image/types"
|
||||
)
|
||||
|
||||
type RegistryService struct {
|
||||
}
|
||||
|
||||
func (*RegistryService) Login(domain, username, passwd string) error {
|
||||
return exec.Cmd("bash", "-c", fmt.Sprintf("buildah login --tls-verify=false --username %s --password %s %s", username, passwd, domain))
|
||||
}
|
||||
func (*RegistryService) Logout(domain string) error {
|
||||
return exec.Cmd("bash", "-c", fmt.Sprintf("buildah logout %s", domain))
|
||||
}
|
||||
func (*RegistryService) Pull(platform v1.Platform, policy string, images ...string) error {
|
||||
platformCmd := fmt.Sprintf(" --platform %s", fmt.Sprintf("%s/%s", platform.OS, platform.Architecture))
|
||||
logger.Info("pull images %v for platform is %s", images, fmt.Sprintf("%s/%s", platform.OS, platform.Architecture))
|
||||
|
||||
for _, image := range images {
|
||||
if err := exec.Cmd("bash", "-c", fmt.Sprintf("buildah pull --tls-verify=false --policy=%s %s %s", policy, platformCmd, image)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*RegistryService) Push(image string) error {
|
||||
return exec.Cmd("bash", "-c", fmt.Sprintf("buildah push --tls-verify=false %s", image))
|
||||
}
|
||||
|
||||
func NewRegistryService() (types.RegistryService, error) {
|
||||
return &RegistryService{}, nil
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
Copyright 2022 cuisongliu@qq.com.
|
||||
|
||||
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 image
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/apimachinery/pkg/util/json"
|
||||
|
||||
"github.com/labring/sealos/pkg/utils/exec"
|
||||
fileutil "github.com/labring/sealos/pkg/utils/file"
|
||||
)
|
||||
|
||||
func initBuildah() (bool, error) {
|
||||
err := buildahPolicySync()
|
||||
if err != nil {
|
||||
return false, errors.New("create policy.json failed")
|
||||
}
|
||||
err = buildahStorageSync()
|
||||
if err != nil {
|
||||
return false, errors.New("create storage config failed")
|
||||
}
|
||||
err = buildahRegistrySync()
|
||||
if err != nil {
|
||||
return false, errors.New("create registry config failed")
|
||||
}
|
||||
_, ok := exec.CheckCmdIsExist("buildah")
|
||||
if ok {
|
||||
return checkBuildahVersion()
|
||||
}
|
||||
return ok, nil
|
||||
}
|
||||
|
||||
var buildahOnce sync.Once
|
||||
var onceBuildahVersion BuildahVersion
|
||||
|
||||
func checkBuildahVersion() (bool, error) {
|
||||
buildahOnce.Do(func() {
|
||||
versionJSON, _ := exec.RunBashCmd("buildah version")
|
||||
var bVersion BuildahVersion
|
||||
_ = json.Unmarshal([]byte(versionJSON), &bVersion)
|
||||
logger.Debug("buildah version: %+v", bVersion)
|
||||
onceBuildahVersion = bVersion
|
||||
})
|
||||
if onceBuildahVersion.GitCommit == "bc5080cc" {
|
||||
return true, nil
|
||||
}
|
||||
return false, errors.New("System's buildah is not match sealos buildah requirements, please uninstall system's buildah, and retry")
|
||||
}
|
||||
|
||||
type BuildahVersion struct {
|
||||
Version string `json:"version"` //1.25.0-dev
|
||||
GoVersion string `json:"goVersion"` //go1.16.9
|
||||
ImageSpec string `json:"imageSpec"` //1.0.2-dev
|
||||
RuntimeSpec string `json:"runtimeSpec"` //1.0.2-dev
|
||||
CniSpec string `json:"cniSpec"` //1.0.0
|
||||
LibcniVersion string `json:"libcniVersion"` //v1.0.1
|
||||
ImageVersion string `json:"imageVersion"` //5.20.0
|
||||
GitCommit string `json:"gitCommit"` //bc5080cc
|
||||
Built string `json:"built"` //Fri Apr 1 17:38:55 2022
|
||||
OsArch string `json:"osArch"` //linux/arm64
|
||||
BuildPlatform string `json:"buildPlatform"` //linux/arm64/v8
|
||||
}
|
||||
|
||||
func buildahPolicySync() error {
|
||||
policyJSONPath := "/etc/containers/policy.json"
|
||||
data := `{
|
||||
"default": [
|
||||
{
|
||||
"type": "insecureAcceptAnything"
|
||||
}
|
||||
],
|
||||
"transports":
|
||||
{
|
||||
"docker-daemon":
|
||||
{
|
||||
"": [{"type":"insecureAcceptAnything"}]
|
||||
}
|
||||
}
|
||||
}`
|
||||
if !fileutil.IsExist(policyJSONPath) {
|
||||
return fileutil.WriteFile(policyJSONPath, []byte(data))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildahStorageSync() error {
|
||||
storagePath := "/var/lib/containers/storage"
|
||||
if err := fileutil.MkDirs(storagePath); err != nil {
|
||||
return errors.Wrap(err, "mkdir buildah storage failed")
|
||||
}
|
||||
data := `[storage]
|
||||
# Default Storage Driver, Must be set for proper operation.
|
||||
driver = "overlay"
|
||||
# Temporary storage location
|
||||
runroot = "/run/containers/storage"
|
||||
# Primary Read/Write location of container storage
|
||||
# When changing the graphroot location on an SELINUX system, you must
|
||||
# ensure the labeling matches the default locations labels with the
|
||||
# following commands:
|
||||
# semanage fcontext -a -e /var/lib/containers/storage /NEWSTORAGEPATH
|
||||
# restorecon -R -v /NEWSTORAGEPATH
|
||||
graphroot = "/var/lib/containers/storage"`
|
||||
storageEtcPath := "/etc/containers/storage.conf"
|
||||
if !fileutil.IsExist(storageEtcPath) {
|
||||
return fileutil.WriteFile(storageEtcPath, []byte(data))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildahRegistrySync() error {
|
||||
registryPath := "/etc/containers/registries.conf"
|
||||
data := `unqualified-search-registries = ["docker.io"]
|
||||
|
||||
[[registry]]
|
||||
prefix = "docker.io/labring"
|
||||
location = "docker.io/labring"
|
||||
`
|
||||
return fileutil.WriteFile(registryPath, []byte(data))
|
||||
}
|
||||
@@ -1,278 +0,0 @@
|
||||
// Copyright © 2022 buildah.
|
||||
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 buildah
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/buildah"
|
||||
"github.com/containers/storage"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
//var containersHeader = map[string]string{
|
||||
// "ContainerName": "CONTAINER NAME",
|
||||
// "ContainerID": "CONTAINER ID",
|
||||
// "Builder": "BUILDER",
|
||||
// "ImageID": "IMAGE ID",
|
||||
// "ImageName": "IMAGE NAME",
|
||||
//}
|
||||
|
||||
type containerFilterParams struct {
|
||||
id string
|
||||
name string
|
||||
ancestor string
|
||||
}
|
||||
|
||||
type containersResults struct {
|
||||
all bool
|
||||
filter string
|
||||
format string
|
||||
json bool
|
||||
noheading bool
|
||||
notruncate bool
|
||||
quiet bool
|
||||
}
|
||||
|
||||
type containerOptions struct {
|
||||
all bool
|
||||
format string
|
||||
json bool
|
||||
noHeading bool
|
||||
noTruncate bool
|
||||
quiet bool
|
||||
}
|
||||
|
||||
type jsonContainer struct {
|
||||
ID string `json:"id"`
|
||||
Builder bool `json:"builder"`
|
||||
ImageID string `json:"imageid"`
|
||||
ImageName string `json:"imagename"`
|
||||
ContainerName string `json:"containername"`
|
||||
}
|
||||
|
||||
//type containerOutputParams struct {
|
||||
// ContainerID string
|
||||
// Builder string
|
||||
// ImageID string
|
||||
// ImageName string
|
||||
// ContainerName string
|
||||
//}
|
||||
|
||||
func GetContainers() ([]byte, error) {
|
||||
globalFlagResults := newGlobalOptions()
|
||||
store, err := getStore(globalFlagResults)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
iopts := containersResults{
|
||||
all: false,
|
||||
filter: "name=",
|
||||
format: "",
|
||||
json: true,
|
||||
noheading: false,
|
||||
notruncate: false,
|
||||
quiet: false,
|
||||
}
|
||||
|
||||
opts := containerOptions{
|
||||
all: iopts.all,
|
||||
format: iopts.format,
|
||||
json: iopts.json,
|
||||
noHeading: iopts.noheading,
|
||||
noTruncate: iopts.notruncate,
|
||||
quiet: iopts.quiet,
|
||||
}
|
||||
|
||||
var params *containerFilterParams
|
||||
|
||||
params, err = parseCtrFilter(iopts.filter)
|
||||
if err != nil {
|
||||
return []byte{}, errors.Wrapf(err, "error parsing filter")
|
||||
}
|
||||
|
||||
if !opts.noHeading && !opts.quiet && opts.format == "" && !opts.json {
|
||||
containerOutputHeader(!opts.noTruncate)
|
||||
}
|
||||
return outputContainers(store, opts, params)
|
||||
}
|
||||
|
||||
// default return json
|
||||
func outputContainers(store storage.Store, opts containerOptions, params *containerFilterParams) ([]byte, error) {
|
||||
seenImages := make(map[string]string)
|
||||
imageNameForID := func(id string) string {
|
||||
if id == "" {
|
||||
return buildah.BaseImageFakeName
|
||||
}
|
||||
imageName, ok := seenImages[id]
|
||||
if ok {
|
||||
return imageName
|
||||
}
|
||||
img, err2 := store.Image(id)
|
||||
if err2 == nil && len(img.Names) > 0 {
|
||||
seenImages[id] = img.Names[0]
|
||||
}
|
||||
return seenImages[id]
|
||||
}
|
||||
|
||||
builders, err := openBuilders(store)
|
||||
if err != nil {
|
||||
return []byte{}, errors.Wrapf(err, "error reading build containers")
|
||||
}
|
||||
var (
|
||||
//containerOutput []containerOutputParams
|
||||
JSONContainers []jsonContainer
|
||||
)
|
||||
if !opts.all {
|
||||
// only output containers created by buildah
|
||||
for _, builder := range builders {
|
||||
image := imageNameForID(builder.FromImageID)
|
||||
if !matchesCtrFilter(builder.ContainerID, builder.Container, builder.FromImageID, image, params) {
|
||||
continue
|
||||
}
|
||||
if opts.json {
|
||||
JSONContainers = append(JSONContainers, jsonContainer{ID: builder.ContainerID,
|
||||
Builder: true,
|
||||
ImageID: builder.FromImageID,
|
||||
ImageName: image,
|
||||
ContainerName: builder.Container})
|
||||
continue
|
||||
}
|
||||
//output := containerOutputParams{
|
||||
// ContainerID: builder.ContainerID,
|
||||
// Builder: " *",
|
||||
// ImageID: builder.FromImageID,
|
||||
// ImageName: image,
|
||||
// ContainerName: builder.Container,
|
||||
//}
|
||||
//containerOutput = append(containerOutput, output)
|
||||
}
|
||||
}
|
||||
// default,json
|
||||
if opts.json {
|
||||
data, err := json.MarshalIndent(JSONContainers, "", " ")
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
//fmt.Printf("%s\n", data)
|
||||
return data, nil
|
||||
}
|
||||
|
||||
//if opts.format != "" {
|
||||
// out := formats.StdoutTemplateArray{Output: containersToGeneric(containerOutput), Template: opts.format, Fields: containersHeader}
|
||||
// return formats.Writer(out).Out()
|
||||
//}
|
||||
|
||||
//for _, ctr := range containerOutput {
|
||||
// if opts.quiet {
|
||||
// fmt.Printf("%-64s\n", ctr.ContainerID)
|
||||
// continue
|
||||
// }
|
||||
// containerOutputUsingFormatString(!opts.noTruncate, ctr)
|
||||
//}
|
||||
return []byte{}, nil
|
||||
}
|
||||
|
||||
//func containersToGeneric(templParams []containerOutputParams) (genericParams []interface{}) {
|
||||
// if len(templParams) > 0 {
|
||||
// for _, v := range templParams {
|
||||
// genericParams = append(genericParams, interface{}(v))
|
||||
// }
|
||||
// }
|
||||
// return genericParams
|
||||
//}
|
||||
|
||||
func parseCtrFilter(filter string) (*containerFilterParams, error) {
|
||||
params := new(containerFilterParams)
|
||||
filters := strings.Split(filter, ",")
|
||||
for _, param := range filters {
|
||||
pair := strings.SplitN(param, "=", 2)
|
||||
if len(pair) != 2 {
|
||||
return nil, errors.Errorf("incorrect filter value %q, should be of form filter=value", param)
|
||||
}
|
||||
switch strings.TrimSpace(pair[0]) {
|
||||
case "id":
|
||||
params.id = pair[1]
|
||||
case "name":
|
||||
params.name = pair[1]
|
||||
case "ancestor":
|
||||
params.ancestor = pair[1]
|
||||
default:
|
||||
return nil, errors.Errorf("invalid filter %q", pair[0])
|
||||
}
|
||||
}
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func containerOutputHeader(truncate bool) {
|
||||
if truncate {
|
||||
fmt.Printf("%-12s %-8s %-12s %-32s %s\n", "CONTAINER ID", "BUILDER", "IMAGE ID", "IMAGE NAME", "CONTAINER NAME")
|
||||
} else {
|
||||
fmt.Printf("%-64s %-8s %-64s %-32s %s\n", "CONTAINER ID", "BUILDER", "IMAGE ID", "IMAGE NAME", "CONTAINER NAME")
|
||||
}
|
||||
}
|
||||
|
||||
func matchesCtrFilter(ctrID, ctrName, imgID, imgName string, params *containerFilterParams) bool {
|
||||
if params == nil {
|
||||
return true
|
||||
}
|
||||
if params.id != "" && !matchesID(ctrID, params.id) {
|
||||
return false
|
||||
}
|
||||
if params.name != "" && !matchesCtrName(ctrName, params.name) {
|
||||
return false
|
||||
}
|
||||
if params.ancestor != "" && !matchesAncestor(imgName, imgID, params.ancestor) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func matchesID(imageID, argID string) bool {
|
||||
return strings.HasPrefix(imageID, argID)
|
||||
}
|
||||
|
||||
func matchesCtrName(ctrName, argName string) bool {
|
||||
return strings.Contains(ctrName, argName)
|
||||
}
|
||||
|
||||
func matchesAncestor(imgName, imgID, argName string) bool {
|
||||
if matchesID(imgID, argName) {
|
||||
return true
|
||||
}
|
||||
return matchesReference(imgName, argName)
|
||||
}
|
||||
|
||||
func matchesReference(name, argName string) bool {
|
||||
if argName == "" {
|
||||
return true
|
||||
}
|
||||
splitName := strings.Split(name, ":")
|
||||
// If the arg contains a tag, we handle it differently than if it does not
|
||||
if strings.Contains(argName, ":") {
|
||||
splitArg := strings.Split(argName, ":")
|
||||
return strings.HasSuffix(splitName[0], splitArg[0]) && (splitName[1] == splitArg[1])
|
||||
}
|
||||
return strings.HasSuffix(splitName[0], argName)
|
||||
}
|
||||
|
||||
//func containerOutputUsingFormatString(truncate bool, params containerOutputParams) {
|
||||
// if truncate {
|
||||
// fmt.Printf("%-12.12s %-8s %-12.12s %-32s %s\n", params.ContainerID, params.Builder, params.ImageID, util.TruncateString(params.ImageName, 32), params.ContainerName)
|
||||
// } else {
|
||||
// fmt.Printf("%-64s %-8s %-64s %-32s %s\n", params.ContainerID, params.Builder, params.ImageID, params.ImageName, params.ContainerName)
|
||||
// }
|
||||
//}
|
||||
@@ -1,295 +0,0 @@
|
||||
// Copyright © 2022 buildah.
|
||||
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 buildah
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/buildah"
|
||||
"github.com/containers/buildah/define"
|
||||
buildahcli "github.com/containers/buildah/pkg/cli"
|
||||
"github.com/containers/buildah/pkg/parse"
|
||||
"github.com/containers/common/pkg/auth"
|
||||
"github.com/containers/common/pkg/config"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
)
|
||||
|
||||
type fromReply struct {
|
||||
authfile string
|
||||
certDir string
|
||||
cidfile string
|
||||
creds string
|
||||
format string
|
||||
name string
|
||||
pull string
|
||||
pullAlways bool
|
||||
pullNever bool
|
||||
quiet bool
|
||||
signaturePolicy string
|
||||
tlsVerify bool
|
||||
*buildahcli.FromAndBudResults
|
||||
*buildahcli.UserNSResults
|
||||
*buildahcli.NameSpaceResults
|
||||
}
|
||||
|
||||
var suffix string
|
||||
|
||||
func From(containerName, imageName string) error {
|
||||
defaultContainerConfig, err := config.Default()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to get container config")
|
||||
}
|
||||
|
||||
var iopts fromReply
|
||||
|
||||
fromAndBudResults := buildahcli.FromAndBudResults{}
|
||||
userNSResults := buildahcli.UserNSResults{}
|
||||
namespaceResults := buildahcli.NameSpaceResults{}
|
||||
|
||||
iopts.FromAndBudResults = &fromAndBudResults
|
||||
iopts.UserNSResults = &userNSResults
|
||||
iopts.NameSpaceResults = &namespaceResults
|
||||
|
||||
iopts.authfile = auth.GetDefaultAuthFile()
|
||||
iopts.certDir = ""
|
||||
iopts.cidfile = ""
|
||||
iopts.creds = ""
|
||||
iopts.format = defaultFormat()
|
||||
iopts.name = containerName
|
||||
iopts.pull = "never" //pull the image from the registry if newer or not present in store, if false, only pull the image if not present, if always, pull the image even if the named image is present in store, if never, only use the image present in store if available"
|
||||
iopts.pullAlways = false
|
||||
iopts.pullNever = true // default --pull never
|
||||
iopts.quiet = false
|
||||
iopts.signaturePolicy = ""
|
||||
suffix = ""
|
||||
iopts.tlsVerify = false
|
||||
fromAndBudFlags, err := buildahcli.GetFromAndBudFlags(&fromAndBudResults, &userNSResults, &namespaceResults)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := auth.CheckAuthFile(iopts.authfile); err != nil {
|
||||
return err
|
||||
}
|
||||
systemContext, err := getSystemContext(iopts.tlsVerify)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error building system context")
|
||||
}
|
||||
|
||||
//platforms, err := parse.PlatformsFromOptions(c)
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
//if len(platforms) > 1 {
|
||||
// logger.Warn("ignoring platforms other than %+v: %+v", platforms[0], platforms[1:])
|
||||
//}
|
||||
|
||||
pullPolicy := define.PullIfMissing
|
||||
if strings.EqualFold(strings.TrimSpace(iopts.pull), "true") {
|
||||
pullPolicy = define.PullIfNewer
|
||||
}
|
||||
if iopts.pullAlways || strings.EqualFold(strings.TrimSpace(iopts.pull), "always") {
|
||||
pullPolicy = define.PullAlways
|
||||
}
|
||||
if iopts.pullNever || strings.EqualFold(strings.TrimSpace(iopts.pull), "never") {
|
||||
pullPolicy = define.PullNever
|
||||
}
|
||||
logger.Debug("Pull Policy for pull [%v]", pullPolicy)
|
||||
|
||||
signaturePolicy := iopts.signaturePolicy
|
||||
|
||||
globalFlagResults := newGlobalOptions()
|
||||
store, err := getStore(globalFlagResults)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "get store")
|
||||
}
|
||||
|
||||
commonOpts, err := CommonBuildOptionsFromFlagSet(&fromAndBudFlags)
|
||||
//commonOpts, err := parse.CommonBuildOptions(c)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "get CommonBuildOptionsFromFlagSet")
|
||||
}
|
||||
|
||||
isolation, err := parse.IsolationOption(iopts.FromAndBudResults.Isolation)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "IsolationOption")
|
||||
}
|
||||
|
||||
namespaceOptions, networkPolicy, err := NamespaceOptions(&fromAndBudFlags)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error parsing namespace-related options")
|
||||
}
|
||||
usernsOption, idmappingOptions, err := IDMappingOptions(&fromAndBudFlags, getGlobalOptionsFlag())
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error parsing ID mapping options")
|
||||
}
|
||||
namespaceOptions.AddOrReplace(usernsOption...)
|
||||
|
||||
format, err := getFormat(iopts.format)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
devices := define.ContainerDevices{}
|
||||
for _, device := range append(defaultContainerConfig.Containers.Devices, iopts.FromAndBudResults.Devices...) {
|
||||
dev, err := parse.DeviceFromPath(device)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
devices = append(devices, dev...)
|
||||
}
|
||||
|
||||
capabilities, err := defaultContainerConfig.Capabilities("", iopts.FromAndBudResults.CapAdd, iopts.FromAndBudResults.CapDrop)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
commonOpts.Ulimit = append(defaultContainerConfig.Containers.DefaultUlimits, commonOpts.Ulimit...)
|
||||
|
||||
decConfig, err := getDecryptConfig(iopts.FromAndBudResults.DecryptionKeys)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to obtain decrypt config")
|
||||
}
|
||||
|
||||
options := buildah.BuilderOptions{
|
||||
FromImage: imageName,
|
||||
Container: iopts.name,
|
||||
ContainerSuffix: suffix,
|
||||
PullPolicy: pullPolicy,
|
||||
SignaturePolicyPath: signaturePolicy,
|
||||
SystemContext: systemContext,
|
||||
DefaultMountsFilePath: globalFlagResults.DefaultMountsFile,
|
||||
Isolation: isolation,
|
||||
NamespaceOptions: namespaceOptions,
|
||||
ConfigureNetwork: networkPolicy,
|
||||
CNIPluginPath: iopts.NameSpaceResults.CNIPlugInPath,
|
||||
CNIConfigDir: iopts.NameSpaceResults.CNIConfigDir,
|
||||
IDMappingOptions: idmappingOptions,
|
||||
Capabilities: capabilities,
|
||||
CommonBuildOpts: commonOpts,
|
||||
Format: format,
|
||||
BlobDirectory: iopts.FromAndBudResults.BlobCache,
|
||||
Devices: devices,
|
||||
MaxPullRetries: maxPullPushRetries,
|
||||
PullRetryDelay: pullPushRetryDelay,
|
||||
OciDecryptConfig: decConfig,
|
||||
}
|
||||
|
||||
if !iopts.quiet {
|
||||
options.ReportWriter = os.Stderr
|
||||
}
|
||||
|
||||
builder, err := buildah.NewBuilder(getContext(), store, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := onBuild(builder, iopts.quiet); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if iopts.cidfile != "" {
|
||||
filePath := iopts.cidfile
|
||||
if err := os.WriteFile(filePath, []byte(builder.ContainerID), 0644); err != nil {
|
||||
return errors.Wrapf(err, "filed to write Container ID File %q", filePath)
|
||||
}
|
||||
}
|
||||
fmt.Printf("%s\n", builder.Container)
|
||||
return builder.Save()
|
||||
}
|
||||
|
||||
func onBuild(builder *buildah.Builder, quiet bool) error {
|
||||
ctr := 0
|
||||
for _, onBuildSpec := range builder.OnBuild() {
|
||||
ctr = ctr + 1
|
||||
commands := strings.Split(onBuildSpec, " ")
|
||||
command := strings.ToUpper(commands[0])
|
||||
args := commands[1:]
|
||||
if !quiet {
|
||||
fmt.Fprintf(os.Stderr, "STEP %d: %s\n", ctr, onBuildSpec)
|
||||
}
|
||||
switch command {
|
||||
case "ADD":
|
||||
case "COPY":
|
||||
dest := ""
|
||||
size := len(args)
|
||||
if size > 1 {
|
||||
dest = args[size-1]
|
||||
args = args[:size-1]
|
||||
}
|
||||
if err := builder.Add(dest, command == "ADD", buildah.AddAndCopyOptions{}, args...); err != nil {
|
||||
return err
|
||||
}
|
||||
case "ANNOTATION":
|
||||
annotation := strings.SplitN(args[0], "=", 2)
|
||||
if len(annotation) > 1 {
|
||||
builder.SetAnnotation(annotation[0], annotation[1])
|
||||
} else {
|
||||
builder.UnsetAnnotation(annotation[0])
|
||||
}
|
||||
case "CMD":
|
||||
builder.SetCmd(args)
|
||||
case "ENV":
|
||||
env := strings.SplitN(args[0], "=", 2)
|
||||
if len(env) > 1 {
|
||||
builder.SetEnv(env[0], env[1])
|
||||
} else {
|
||||
builder.UnsetEnv(env[0])
|
||||
}
|
||||
case "ENTRYPOINT":
|
||||
builder.SetEntrypoint(args)
|
||||
case "EXPOSE":
|
||||
builder.SetPort(strings.Join(args, " "))
|
||||
case "HOSTNAME":
|
||||
builder.SetHostname(strings.Join(args, " "))
|
||||
case "LABEL":
|
||||
label := strings.SplitN(args[0], "=", 2)
|
||||
if len(label) > 1 {
|
||||
builder.SetLabel(label[0], label[1])
|
||||
} else {
|
||||
builder.UnsetLabel(label[0])
|
||||
}
|
||||
case "MAINTAINER":
|
||||
builder.SetMaintainer(strings.Join(args, " "))
|
||||
case "ONBUILD":
|
||||
builder.SetOnBuild(strings.Join(args, " "))
|
||||
case "RUN":
|
||||
var stdout io.Writer
|
||||
if quiet {
|
||||
stdout = io.Discard
|
||||
}
|
||||
if err := builder.Run(args, buildah.RunOptions{Stdout: stdout}); err != nil {
|
||||
return err
|
||||
}
|
||||
case "SHELL":
|
||||
builder.SetShell(args)
|
||||
case "STOPSIGNAL":
|
||||
builder.SetStopSignal(strings.Join(args, " "))
|
||||
case "USER":
|
||||
builder.SetUser(strings.Join(args, " "))
|
||||
case "VOLUME":
|
||||
builder.AddVolume(strings.Join(args, " "))
|
||||
case "WORKINGDIR":
|
||||
builder.SetWorkDir(strings.Join(args, " "))
|
||||
default:
|
||||
logger.Error("unknown OnBuild command %q; ignored", onBuildSpec)
|
||||
}
|
||||
}
|
||||
builder.ClearOnBuild()
|
||||
return nil
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
// Copyright © 2022 buildah.
|
||||
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 buildah
|
||||
|
||||
// nosemgrep: go.lang.security.audit.xss.import-text-template.import-text-template
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"text/template"
|
||||
|
||||
"github.com/containers/buildah"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
const (
|
||||
inspectTypeContainer = "container"
|
||||
inspectTypeImage = "image"
|
||||
inspectTypeManifest = "manifest"
|
||||
)
|
||||
|
||||
type inspectResults struct {
|
||||
format string
|
||||
inspectType string
|
||||
}
|
||||
|
||||
func Inspect(name string) (buildah.BuilderInfo, error) {
|
||||
var iopts inspectResults
|
||||
iopts.format = ""
|
||||
iopts.inspectType = inspectTypeContainer
|
||||
|
||||
var builder *buildah.Builder
|
||||
|
||||
systemContext, err := getSystemContext(false)
|
||||
if err != nil {
|
||||
return buildah.BuilderInfo{}, errors.Wrapf(err, "error building system context")
|
||||
}
|
||||
|
||||
globalFlagResults := newGlobalOptions()
|
||||
store, err := getStore(globalFlagResults)
|
||||
if err != nil {
|
||||
return buildah.BuilderInfo{}, err
|
||||
}
|
||||
|
||||
ctx := getContext()
|
||||
|
||||
switch iopts.inspectType {
|
||||
case inspectTypeContainer:
|
||||
builder, err = openBuilder(ctx, store, name)
|
||||
if err != nil {
|
||||
//if c.Flag("type").Changed {
|
||||
// return errors.Wrapf(err, "error reading build container")
|
||||
//}
|
||||
builder, err = openImage(ctx, systemContext, store, name)
|
||||
if err != nil {
|
||||
if manifestErr := manifestInspect(ctx, store, systemContext, name); manifestErr == nil {
|
||||
return buildah.BuilderInfo{}, nil
|
||||
}
|
||||
return buildah.BuilderInfo{}, err
|
||||
}
|
||||
}
|
||||
case inspectTypeImage:
|
||||
builder, err = openImage(ctx, systemContext, store, name)
|
||||
if err != nil {
|
||||
return buildah.BuilderInfo{}, err
|
||||
}
|
||||
case inspectTypeManifest:
|
||||
return buildah.BuilderInfo{}, manifestInspect(ctx, store, systemContext, name)
|
||||
default:
|
||||
return buildah.BuilderInfo{}, errors.Errorf("the only recognized types are %q and %q", inspectTypeContainer, inspectTypeImage)
|
||||
}
|
||||
out := buildah.GetBuildInfo(builder)
|
||||
if iopts.format != "" {
|
||||
format := iopts.format
|
||||
if matched, err := regexp.MatchString("{{.*}}", format); err != nil {
|
||||
return buildah.BuilderInfo{}, errors.Wrapf(err, "error validating format provided: %s", format)
|
||||
} else if !matched {
|
||||
return buildah.BuilderInfo{}, errors.Errorf("error invalid format provided: %s", format)
|
||||
}
|
||||
t, err := template.New("format").Parse(format)
|
||||
if err != nil {
|
||||
return buildah.BuilderInfo{}, errors.Wrapf(err, "Template parsing error")
|
||||
}
|
||||
if err = t.Execute(os.Stdout, out); err != nil {
|
||||
return buildah.BuilderInfo{}, err
|
||||
}
|
||||
if term.IsTerminal(int(os.Stdout.Fd())) {
|
||||
fmt.Println()
|
||||
}
|
||||
return buildah.BuilderInfo{}, nil
|
||||
}
|
||||
|
||||
//enc := json.NewEncoder(os.Stdout)
|
||||
//enc.SetIndent("", " ")
|
||||
//if term.IsTerminal(int(os.Stdout.Fd())) {
|
||||
// enc.SetEscapeHTML(false)
|
||||
//}
|
||||
//return enc.Encode(out)
|
||||
return out, nil
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
// Copyright © 2022 buildah.
|
||||
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 buildah
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type jsonMount struct {
|
||||
Container string `json:"container,omitempty"`
|
||||
MountPoint string `json:"mountPoint"`
|
||||
}
|
||||
|
||||
type mountOptions struct {
|
||||
json bool
|
||||
}
|
||||
|
||||
func Mount(name string) error {
|
||||
globalFlagResults := newGlobalOptions()
|
||||
store, err := getStore(globalFlagResults)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
opts := mountOptions{
|
||||
json: false,
|
||||
}
|
||||
var jsonMounts []jsonMount
|
||||
var lastError error
|
||||
|
||||
args := []string{name}
|
||||
if len(args) > 0 {
|
||||
// Do not allow to mount a graphdriver that is not vfs if we are creating the userns as part
|
||||
// of the mount command.
|
||||
// Differently, allow the mount if we are already in a userns, as the mount point will still
|
||||
// be accessible once "buildah mount" exits.
|
||||
if os.Geteuid() != 0 && store.GraphDriverName() != "vfs" {
|
||||
return errors.Errorf("cannot mount using driver %s in rootless mode. You need to run it in a `buildah unshare` session", store.GraphDriverName())
|
||||
}
|
||||
|
||||
for _, name := range args {
|
||||
builder, err := openBuilder(getContext(), store, name)
|
||||
if err != nil {
|
||||
if lastError != nil {
|
||||
fmt.Fprintln(os.Stderr, lastError)
|
||||
}
|
||||
lastError = errors.Wrapf(err, "error reading build container %q", name)
|
||||
continue
|
||||
}
|
||||
mountPoint, err := builder.Mount(builder.MountLabel)
|
||||
if err != nil {
|
||||
if lastError != nil {
|
||||
fmt.Fprintln(os.Stderr, lastError)
|
||||
}
|
||||
lastError = errors.Wrapf(err, "error mounting %q container %q", name, builder.Container)
|
||||
continue
|
||||
}
|
||||
if len(args) > 1 {
|
||||
if opts.json {
|
||||
jsonMounts = append(jsonMounts, jsonMount{Container: name, MountPoint: mountPoint})
|
||||
continue
|
||||
}
|
||||
fmt.Printf("%s %s\n", name, mountPoint)
|
||||
} else {
|
||||
if opts.json {
|
||||
jsonMounts = append(jsonMounts, jsonMount{MountPoint: mountPoint})
|
||||
continue
|
||||
}
|
||||
//fmt.Printf("%s\n", mountPoint)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
builders, err := openBuilders(store)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error reading build containers")
|
||||
}
|
||||
|
||||
for _, builder := range builders {
|
||||
mounted, err := builder.Mounted()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if mounted {
|
||||
if opts.json {
|
||||
jsonMounts = append(jsonMounts, jsonMount{Container: builder.Container, MountPoint: builder.MountPoint})
|
||||
continue
|
||||
}
|
||||
fmt.Printf("%s %s\n", builder.Container, builder.MountPoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if opts.json {
|
||||
data, err := json.MarshalIndent(jsonMounts, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%s\n", data)
|
||||
}
|
||||
|
||||
return lastError
|
||||
}
|
||||
@@ -1,656 +0,0 @@
|
||||
// Copyright © 2022 buildah.
|
||||
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 parse
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/common/pkg/parse"
|
||||
"github.com/containers/image/v5/types"
|
||||
"github.com/containers/storage"
|
||||
"github.com/containers/storage/pkg/idtools"
|
||||
"github.com/containers/storage/pkg/lockfile"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
// TypeBind is the type for mounting host dir
|
||||
TypeBind = "bind"
|
||||
// TypeTmpfs is the type for mounting tmpfs
|
||||
TypeTmpfs = "tmpfs"
|
||||
// TypeCache is the type for mounting a common persistent cache from host
|
||||
TypeCache = "cache"
|
||||
// mount=type=cache must create a persistent directory on host so its available for all consecutive builds.
|
||||
// Lifecycle of following directory will be inherited from how host machine treats temporary directory
|
||||
BuildahCacheDir = "buildah-cache"
|
||||
// mount=type=cache allows users to lock a cache store while its being used by another build
|
||||
BuildahCacheLockfile = "buildah-cache-lockfile"
|
||||
|
||||
RO = "ro"
|
||||
NoSuid = "nosuid"
|
||||
NoDev = "nodev"
|
||||
NoExec = "noexec"
|
||||
Shared = "shared"
|
||||
Rshared = "rshared"
|
||||
APrivate = "private"
|
||||
Rprivate = "rprivate"
|
||||
Slave = "slave"
|
||||
Rslave = "rslave"
|
||||
A1 = "Z"
|
||||
A2 = "z"
|
||||
A3 = "U"
|
||||
|
||||
Target = "target"
|
||||
Dst = "dst"
|
||||
Destination = "destination"
|
||||
|
||||
RW = "rw"
|
||||
ReadWrite = "readwrite"
|
||||
|
||||
ReadOnly = "readonly"
|
||||
|
||||
SRC = "src"
|
||||
Source = "source"
|
||||
)
|
||||
|
||||
var (
|
||||
errBadMntOption = errors.New("invalid mount option")
|
||||
errBadOptionArg = errors.New("must provide an argument for option")
|
||||
errBadVolDest = errors.New("must set volume destination")
|
||||
errBadVolSrc = errors.New("must set volume source")
|
||||
errDuplicateDest = errors.New("duplicate mount destination")
|
||||
)
|
||||
|
||||
// GetBindMount parses a single bind mount entry from the --mount flag.
|
||||
// Returns specifiedMount and a string which contains name of image that we mounted otherwise its empty.
|
||||
// Caller is expected to perform unmount of any mounted images
|
||||
func GetBindMount(ctx *types.SystemContext, args []string, contextDir string, store storage.Store, imageMountLabel string, additionalMountPoints map[string]StageMountDetails) (specs.Mount, string, error) {
|
||||
newMount := specs.Mount{
|
||||
Type: TypeBind,
|
||||
}
|
||||
|
||||
mountReadability := false
|
||||
setDest := false
|
||||
bindNonRecursive := false
|
||||
fromImage := ""
|
||||
|
||||
for _, val := range args {
|
||||
kv := strings.SplitN(val, "=", 2)
|
||||
switch kv[0] {
|
||||
case "bind-nonrecursive":
|
||||
newMount.Options = append(newMount.Options, "bind")
|
||||
bindNonRecursive = true
|
||||
//case "ro", "nosuid", "nodev", "noexec":
|
||||
case RO, NoSuid, NoDev, NoExec:
|
||||
// TODO: detect duplication of these options.
|
||||
// (Is this necessary?)
|
||||
newMount.Options = append(newMount.Options, kv[0])
|
||||
mountReadability = true
|
||||
//case "rw", "readwrite":
|
||||
case RW, ReadWrite:
|
||||
newMount.Options = append(newMount.Options, "rw")
|
||||
mountReadability = true
|
||||
//case "readonly":
|
||||
case ReadOnly:
|
||||
// Alias for "ro"
|
||||
newMount.Options = append(newMount.Options, "ro")
|
||||
mountReadability = true
|
||||
//case "shared", "rshared", "private", "rprivate", "slave", "rslave", "Z", "z", "U":
|
||||
case Shared, Rshared, APrivate, Rprivate, Slave, Rslave, A1, A2, A3:
|
||||
newMount.Options = append(newMount.Options, kv[0])
|
||||
case "from":
|
||||
if len(kv) == 1 {
|
||||
return newMount, "", errors.Wrapf(errBadOptionArg, kv[0])
|
||||
}
|
||||
fromImage = kv[1]
|
||||
case "bind-propagation":
|
||||
if len(kv) == 1 {
|
||||
return newMount, "", errors.Wrapf(errBadOptionArg, kv[0])
|
||||
}
|
||||
newMount.Options = append(newMount.Options, kv[1])
|
||||
//case "src", "source":
|
||||
case SRC, Source:
|
||||
if len(kv) == 1 {
|
||||
return newMount, "", errors.Wrapf(errBadOptionArg, kv[0])
|
||||
}
|
||||
newMount.Source = kv[1]
|
||||
//case "target", "dst", "destination":
|
||||
case Target, Dst, Destination:
|
||||
if len(kv) == 1 {
|
||||
return newMount, "", errors.Wrapf(errBadOptionArg, kv[0])
|
||||
}
|
||||
if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil {
|
||||
return newMount, "", err
|
||||
}
|
||||
newMount.Destination = kv[1]
|
||||
setDest = true
|
||||
case "consistency":
|
||||
// Option for OS X only, has no meaning on other platforms
|
||||
// and can thus be safely ignored.
|
||||
// See also the handling of the equivalent "delegated" and "cached" in ValidateVolumeOpts
|
||||
default:
|
||||
return newMount, "", errors.Wrapf(errBadMntOption, kv[0])
|
||||
}
|
||||
}
|
||||
|
||||
// default mount readability is always readonly
|
||||
if !mountReadability {
|
||||
newMount.Options = append(newMount.Options, "ro")
|
||||
}
|
||||
|
||||
// Following variable ensures that we return imagename only if we did additional mount
|
||||
isImageMounted := false
|
||||
if fromImage != "" {
|
||||
mountPoint := ""
|
||||
if additionalMountPoints != nil {
|
||||
if val, ok := additionalMountPoints[fromImage]; ok {
|
||||
mountPoint = val.MountPoint
|
||||
}
|
||||
}
|
||||
// if mountPoint of image was not found in additionalMap
|
||||
// or additionalMap was nil, try mounting image
|
||||
if mountPoint == "" {
|
||||
image, err := LookupImage(ctx, store, fromImage)
|
||||
if err != nil {
|
||||
return newMount, "", err
|
||||
}
|
||||
|
||||
mountPoint, err = image.Mount(context.Background(), nil, imageMountLabel)
|
||||
if err != nil {
|
||||
return newMount, "", err
|
||||
}
|
||||
isImageMounted = true
|
||||
}
|
||||
contextDir = mountPoint
|
||||
}
|
||||
|
||||
// buildkit parity: default bind option must be `rbind`
|
||||
// unless specified
|
||||
if !bindNonRecursive {
|
||||
newMount.Options = append(newMount.Options, "rbind")
|
||||
}
|
||||
|
||||
if !setDest {
|
||||
return newMount, fromImage, errBadVolDest
|
||||
}
|
||||
|
||||
// buildkit parity: support absolute path for sources from current build context
|
||||
if contextDir != "" {
|
||||
// path should be /contextDir/specified path
|
||||
newMount.Source = filepath.Join(contextDir, filepath.Clean(string(filepath.Separator)+newMount.Source))
|
||||
} else {
|
||||
// looks like its coming from `build run --mount=type=bind` allow using absolute path
|
||||
// error out if no source is set
|
||||
if newMount.Source == "" {
|
||||
return newMount, "", errBadVolSrc
|
||||
}
|
||||
if err := parse.ValidateVolumeHostDir(newMount.Source); err != nil {
|
||||
return newMount, "", err
|
||||
}
|
||||
}
|
||||
|
||||
opts, err := parse.ValidateVolumeOpts(newMount.Options)
|
||||
if err != nil {
|
||||
return newMount, fromImage, err
|
||||
}
|
||||
newMount.Options = opts
|
||||
|
||||
if !isImageMounted {
|
||||
// we don't want any cleanups if image was not mounted explicitly
|
||||
// so dont return anything
|
||||
fromImage = ""
|
||||
}
|
||||
|
||||
return newMount, fromImage, nil
|
||||
}
|
||||
|
||||
// GetCacheMount parses a single cache mount entry from the --mount flag.
|
||||
func GetCacheMount(args []string, store storage.Store, imageMountLabel string, additionalMountPoints map[string]StageMountDetails) (specs.Mount, []string, error) {
|
||||
var err error
|
||||
var mode uint64
|
||||
lockedTargets := make([]string, 0)
|
||||
var (
|
||||
setDest bool
|
||||
setShared bool
|
||||
setReadOnly bool
|
||||
)
|
||||
fromStage := ""
|
||||
newMount := specs.Mount{
|
||||
Type: TypeBind,
|
||||
}
|
||||
// if id is set a new subdirectory with `id` will be created under /host-temp/buildah-build-cache/id
|
||||
id := ""
|
||||
//buidkit parity: cache directory defaults to 755
|
||||
mode = 0o755
|
||||
//buidkit parity: cache directory defaults to uid 0 if not specified
|
||||
uid := 0
|
||||
//buidkit parity: cache directory defaults to gid 0 if not specified
|
||||
gid := 0
|
||||
// sharing mode
|
||||
sharing := Shared
|
||||
|
||||
for _, val := range args {
|
||||
kv := strings.SplitN(val, "=", 2)
|
||||
switch kv[0] {
|
||||
case "nosuid", "nodev", "noexec":
|
||||
// TODO: detect duplication of these options.
|
||||
// (Is this necessary?)
|
||||
newMount.Options = append(newMount.Options, kv[0])
|
||||
case "rw", "readwrite":
|
||||
newMount.Options = append(newMount.Options, "rw")
|
||||
case "readonly", "ro":
|
||||
// Alias for "ro"
|
||||
newMount.Options = append(newMount.Options, "ro")
|
||||
setReadOnly = true
|
||||
case "shared", "rshared", "private", "rprivate", "slave", "rslave", "Z", "z", "U":
|
||||
newMount.Options = append(newMount.Options, kv[0])
|
||||
setShared = true
|
||||
case "sharing":
|
||||
sharing = kv[1]
|
||||
case "bind-propagation":
|
||||
if len(kv) == 1 {
|
||||
return newMount, lockedTargets, errors.Wrapf(errBadOptionArg, kv[0])
|
||||
}
|
||||
newMount.Options = append(newMount.Options, kv[1])
|
||||
case "id":
|
||||
if len(kv) == 1 {
|
||||
return newMount, lockedTargets, errors.Wrapf(errBadOptionArg, kv[0])
|
||||
}
|
||||
id = kv[1]
|
||||
case "from":
|
||||
if len(kv) == 1 {
|
||||
return newMount, lockedTargets, errors.Wrapf(errBadOptionArg, kv[0])
|
||||
}
|
||||
fromStage = kv[1]
|
||||
case "target", "dst", "destination":
|
||||
if len(kv) == 1 {
|
||||
return newMount, lockedTargets, errors.Wrapf(errBadOptionArg, kv[0])
|
||||
}
|
||||
if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil {
|
||||
return newMount, lockedTargets, err
|
||||
}
|
||||
newMount.Destination = kv[1]
|
||||
setDest = true
|
||||
case "src", "source":
|
||||
if len(kv) == 1 {
|
||||
return newMount, lockedTargets, errors.Wrapf(errBadOptionArg, kv[0])
|
||||
}
|
||||
newMount.Source = kv[1]
|
||||
case "mode":
|
||||
if len(kv) == 1 {
|
||||
return newMount, lockedTargets, errors.Wrapf(errBadOptionArg, kv[0])
|
||||
}
|
||||
mode, err = strconv.ParseUint(kv[1], 8, 32)
|
||||
if err != nil {
|
||||
return newMount, lockedTargets, errors.Wrapf(err, "Unable to parse cache mode")
|
||||
}
|
||||
case "uid":
|
||||
if len(kv) == 1 {
|
||||
return newMount, lockedTargets, errors.Wrapf(errBadOptionArg, kv[0])
|
||||
}
|
||||
uid, err = strconv.Atoi(kv[1])
|
||||
if err != nil {
|
||||
return newMount, lockedTargets, errors.Wrapf(err, "Unable to parse cache uid")
|
||||
}
|
||||
case "gid":
|
||||
if len(kv) == 1 {
|
||||
return newMount, lockedTargets, errors.Wrapf(errBadOptionArg, kv[0])
|
||||
}
|
||||
gid, err = strconv.Atoi(kv[1])
|
||||
if err != nil {
|
||||
return newMount, lockedTargets, errors.Wrapf(err, "Unable to parse cache gid")
|
||||
}
|
||||
default:
|
||||
return newMount, lockedTargets, errors.Wrapf(errBadMntOption, kv[0])
|
||||
}
|
||||
}
|
||||
|
||||
if !setDest {
|
||||
return newMount, lockedTargets, errBadVolDest
|
||||
}
|
||||
|
||||
if fromStage != "" {
|
||||
// do not create cache on host
|
||||
// instead use read-only mounted stage as cache
|
||||
mountPoint := ""
|
||||
if additionalMountPoints != nil {
|
||||
if val, ok := additionalMountPoints[fromStage]; ok {
|
||||
if val.IsStage {
|
||||
mountPoint = val.MountPoint
|
||||
}
|
||||
}
|
||||
}
|
||||
// Cache does not supports using image so if not stage found
|
||||
// return with error
|
||||
if mountPoint == "" {
|
||||
return newMount, lockedTargets, fmt.Errorf("no stage found with name %s", fromStage)
|
||||
}
|
||||
// path should be /contextDir/specified path
|
||||
newMount.Source = filepath.Join(mountPoint, filepath.Clean(string(filepath.Separator)+newMount.Source))
|
||||
} else {
|
||||
// we need to create cache on host if no image is being used
|
||||
|
||||
// since type is cache and cache can be reused by consecutive builds
|
||||
// create a common cache directory, which persists on hosts within temp lifecycle
|
||||
// add subdirectory if specified
|
||||
|
||||
// cache parent directory
|
||||
cacheParent := filepath.Join(getTempDir(), BuildahCacheDir)
|
||||
// create cache on host if not present
|
||||
err = os.MkdirAll(cacheParent, os.FileMode(0755))
|
||||
if err != nil {
|
||||
return newMount, lockedTargets, errors.Wrapf(err, "Unable to create build cache directory")
|
||||
}
|
||||
|
||||
if id != "" {
|
||||
newMount.Source = filepath.Join(cacheParent, filepath.Clean(id))
|
||||
} else {
|
||||
newMount.Source = filepath.Join(cacheParent, filepath.Clean(newMount.Destination))
|
||||
}
|
||||
idPair := idtools.IDPair{
|
||||
UID: uid,
|
||||
GID: gid,
|
||||
}
|
||||
//buildkit parity: change uid and gid if specified otheriwise keep `0`
|
||||
err = idtools.MkdirAllAndChownNew(newMount.Source, os.FileMode(mode), idPair)
|
||||
if err != nil {
|
||||
return newMount, lockedTargets, errors.Wrapf(err, "Unable to change uid,gid of cache directory")
|
||||
}
|
||||
}
|
||||
|
||||
switch sharing {
|
||||
case "locked":
|
||||
// lock parent cache
|
||||
lockfile, err := lockfile.GetLockfile(filepath.Join(newMount.Source, BuildahCacheLockfile))
|
||||
if err != nil {
|
||||
return newMount, lockedTargets, errors.Wrapf(err, "Unable to acquire lock when sharing mode is locked")
|
||||
}
|
||||
// Will be unlocked after the RUN step is executed.
|
||||
lockfile.Lock()
|
||||
lockedTargets = append(lockedTargets, filepath.Join(newMount.Source, BuildahCacheLockfile))
|
||||
case "shared":
|
||||
// do nothing since default is `shared`
|
||||
break
|
||||
default:
|
||||
// error out for unknown values
|
||||
return newMount, lockedTargets, errors.Wrapf(err, "Unrecognized value %q for field `sharing`", sharing)
|
||||
}
|
||||
|
||||
// buildkit parity: default sharing should be shared
|
||||
// unless specified
|
||||
if !setShared {
|
||||
newMount.Options = append(newMount.Options, "shared")
|
||||
}
|
||||
|
||||
// buildkit parity: cache must writable unless `ro` or `readonly` is configured explicitly
|
||||
if !setReadOnly {
|
||||
newMount.Options = append(newMount.Options, "rw")
|
||||
}
|
||||
|
||||
newMount.Options = append(newMount.Options, "bind")
|
||||
|
||||
opts, err := parse.ValidateVolumeOpts(newMount.Options)
|
||||
if err != nil {
|
||||
return newMount, lockedTargets, err
|
||||
}
|
||||
newMount.Options = opts
|
||||
|
||||
return newMount, lockedTargets, nil
|
||||
}
|
||||
|
||||
// ValidateVolumeMountHostDir validates the host path of buildah --volume
|
||||
func ValidateVolumeMountHostDir(hostDir string) error {
|
||||
if !filepath.IsAbs(hostDir) {
|
||||
return errors.Errorf("invalid host path, must be an absolute path %q", hostDir)
|
||||
}
|
||||
if _, err := os.Stat(hostDir); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RevertEscapedColon converts "\:" to ":"
|
||||
func RevertEscapedColon(source string) string {
|
||||
return strings.ReplaceAll(source, "\\:", ":")
|
||||
}
|
||||
|
||||
// SplitStringWithColonEscape splits string into slice by colon. Backslash-escaped colon (i.e. "\:") will not be regarded as separator
|
||||
func SplitStringWithColonEscape(str string) []string {
|
||||
result := make([]string, 0, 3)
|
||||
sb := &strings.Builder{}
|
||||
for idx, r := range str {
|
||||
if r == ':' {
|
||||
// the colon is backslash-escaped
|
||||
if idx-1 > 0 && str[idx-1] == '\\' {
|
||||
sb.WriteRune(r)
|
||||
} else {
|
||||
// os.Stat will fail if path contains escaped colon
|
||||
result = append(result, RevertEscapedColon(sb.String()))
|
||||
sb.Reset()
|
||||
}
|
||||
} else {
|
||||
sb.WriteRune(r)
|
||||
}
|
||||
}
|
||||
if sb.Len() > 0 {
|
||||
result = append(result, RevertEscapedColon(sb.String()))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func getVolumeMounts(volumes []string) (map[string]specs.Mount, error) {
|
||||
finalVolumeMounts := make(map[string]specs.Mount)
|
||||
|
||||
for _, volume := range volumes {
|
||||
volumeMount, err := Volume(volume)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, ok := finalVolumeMounts[volumeMount.Destination]; ok {
|
||||
return nil, errors.Wrapf(errDuplicateDest, volumeMount.Destination)
|
||||
}
|
||||
finalVolumeMounts[volumeMount.Destination] = volumeMount
|
||||
}
|
||||
return finalVolumeMounts, nil
|
||||
}
|
||||
|
||||
// Volume parses the input of --volume
|
||||
func Volume(volume string) (specs.Mount, error) {
|
||||
mount := specs.Mount{}
|
||||
arr := SplitStringWithColonEscape(volume)
|
||||
if len(arr) < 2 {
|
||||
return mount, errors.Errorf("incorrect volume format %q, should be host-dir:ctr-dir[:option]", volume)
|
||||
}
|
||||
if err := ValidateVolumeMountHostDir(arr[0]); err != nil {
|
||||
return mount, err
|
||||
}
|
||||
if err := parse.ValidateVolumeCtrDir(arr[1]); err != nil {
|
||||
return mount, err
|
||||
}
|
||||
mountOptions := ""
|
||||
if len(arr) > 2 {
|
||||
mountOptions = arr[2]
|
||||
if _, err := parse.ValidateVolumeOpts(strings.Split(arr[2], ",")); err != nil {
|
||||
return mount, err
|
||||
}
|
||||
}
|
||||
mountOpts := strings.Split(mountOptions, ",")
|
||||
mount.Source = arr[0]
|
||||
mount.Destination = arr[1]
|
||||
mount.Type = "rbind"
|
||||
mount.Options = mountOpts
|
||||
return mount, nil
|
||||
}
|
||||
|
||||
// GetVolumes gets the volumes from --volume and --mount
|
||||
func GetVolumes(ctx *types.SystemContext, store storage.Store, volumes []string, mounts []string, contextDir string) ([]specs.Mount, []string, []string, error) {
|
||||
unifiedMounts, mountedImages, lockedTargets, err := getMounts(ctx, store, mounts, contextDir)
|
||||
if err != nil {
|
||||
return nil, mountedImages, lockedTargets, err
|
||||
}
|
||||
volumeMounts, err := getVolumeMounts(volumes)
|
||||
if err != nil {
|
||||
return nil, mountedImages, lockedTargets, err
|
||||
}
|
||||
for dest, mount := range volumeMounts {
|
||||
if _, ok := unifiedMounts[dest]; ok {
|
||||
return nil, mountedImages, lockedTargets, errors.Wrapf(errDuplicateDest, dest)
|
||||
}
|
||||
unifiedMounts[dest] = mount
|
||||
}
|
||||
|
||||
finalMounts := make([]specs.Mount, 0, len(unifiedMounts))
|
||||
for _, mount := range unifiedMounts {
|
||||
finalMounts = append(finalMounts, mount)
|
||||
}
|
||||
return finalMounts, mountedImages, lockedTargets, nil
|
||||
}
|
||||
|
||||
// getMounts takes user-provided input from the --mount flag and creates OCI
|
||||
// spec mounts.
|
||||
// buildah run --mount type=bind,src=/etc/resolv.conf,target=/etc/resolv.conf ...
|
||||
// buildah run --mount type=tmpfs,target=/dev/shm ...
|
||||
func getMounts(ctx *types.SystemContext, store storage.Store, mounts []string, contextDir string) (map[string]specs.Mount, []string, []string, error) {
|
||||
finalMounts := make(map[string]specs.Mount)
|
||||
mountedImages := make([]string, 0)
|
||||
lockedTargets := make([]string, 0)
|
||||
|
||||
errInvalidSyntax := errors.Errorf("incorrect mount format: should be --mount type=<bind|tmpfs>,[src=<host-dir>,]target=<ctr-dir>[,options]")
|
||||
|
||||
// TODO(vrothberg): the manual parsing can be replaced with a regular expression
|
||||
// to allow a more robust parsing of the mount format and to give
|
||||
// precise errors regarding supported format versus supported options.
|
||||
for _, mount := range mounts {
|
||||
arr := strings.SplitN(mount, ",", 2)
|
||||
if len(arr) < 2 {
|
||||
return nil, mountedImages, lockedTargets, errors.Wrapf(errInvalidSyntax, "%q", mount)
|
||||
}
|
||||
kv := strings.Split(arr[0], "=")
|
||||
// TODO: type is not explicitly required in Docker.
|
||||
// If not specified, it defaults to "volume".
|
||||
if len(kv) != 2 || kv[0] != "type" {
|
||||
return nil, mountedImages, lockedTargets, errors.Wrapf(errInvalidSyntax, "%q", mount)
|
||||
}
|
||||
|
||||
tokens := strings.Split(arr[1], ",")
|
||||
switch kv[1] {
|
||||
case TypeBind:
|
||||
mount, image, err := GetBindMount(ctx, tokens, contextDir, store, "", nil)
|
||||
if err != nil {
|
||||
return nil, mountedImages, lockedTargets, err
|
||||
}
|
||||
if _, ok := finalMounts[mount.Destination]; ok {
|
||||
return nil, mountedImages, lockedTargets, errors.Wrapf(errDuplicateDest, mount.Destination)
|
||||
}
|
||||
finalMounts[mount.Destination] = mount
|
||||
mountedImages = append(mountedImages, image)
|
||||
case TypeCache:
|
||||
mount, lockedPaths, err := GetCacheMount(tokens, store, "", nil)
|
||||
lockedTargets = lockedPaths
|
||||
if err != nil {
|
||||
return nil, mountedImages, lockedTargets, err
|
||||
}
|
||||
if _, ok := finalMounts[mount.Destination]; ok {
|
||||
return nil, mountedImages, lockedTargets, errors.Wrapf(errDuplicateDest, mount.Destination)
|
||||
}
|
||||
finalMounts[mount.Destination] = mount
|
||||
case TypeTmpfs:
|
||||
mount, err := GetTmpfsMount(tokens)
|
||||
if err != nil {
|
||||
return nil, mountedImages, lockedTargets, err
|
||||
}
|
||||
if _, ok := finalMounts[mount.Destination]; ok {
|
||||
return nil, mountedImages, lockedTargets, errors.Wrapf(errDuplicateDest, mount.Destination)
|
||||
}
|
||||
finalMounts[mount.Destination] = mount
|
||||
default:
|
||||
return nil, mountedImages, lockedTargets, errors.Errorf("invalid filesystem type %q", kv[1])
|
||||
}
|
||||
}
|
||||
|
||||
return finalMounts, mountedImages, lockedTargets, nil
|
||||
}
|
||||
|
||||
// GetTmpfsMount parses a single tmpfs mount entry from the --mount flag
|
||||
func GetTmpfsMount(args []string) (specs.Mount, error) {
|
||||
newMount := specs.Mount{
|
||||
Type: TypeTmpfs,
|
||||
Source: TypeTmpfs,
|
||||
}
|
||||
|
||||
setDest := false
|
||||
|
||||
for _, val := range args {
|
||||
kv := strings.SplitN(val, "=", 2)
|
||||
switch kv[0] {
|
||||
case "ro", "nosuid", "nodev", "noexec":
|
||||
newMount.Options = append(newMount.Options, kv[0])
|
||||
case "readonly":
|
||||
// Alias for "ro"
|
||||
newMount.Options = append(newMount.Options, "ro")
|
||||
case "tmpcopyup":
|
||||
//the path that is shadowed by the tmpfs mount is recursively copied up to the tmpfs itself.
|
||||
newMount.Options = append(newMount.Options, kv[0])
|
||||
case "tmpfs-mode":
|
||||
if len(kv) == 1 {
|
||||
return newMount, errors.Wrapf(errBadOptionArg, kv[0])
|
||||
}
|
||||
newMount.Options = append(newMount.Options, fmt.Sprintf("mode=%s", kv[1]))
|
||||
case "tmpfs-size":
|
||||
if len(kv) == 1 {
|
||||
return newMount, errors.Wrapf(errBadOptionArg, kv[0])
|
||||
}
|
||||
newMount.Options = append(newMount.Options, fmt.Sprintf("size=%s", kv[1]))
|
||||
case "src", "source":
|
||||
return newMount, errors.Errorf("source is not supported with tmpfs mounts")
|
||||
case "target", "dst", "destination":
|
||||
if len(kv) == 1 {
|
||||
return newMount, errors.Wrapf(errBadOptionArg, kv[0])
|
||||
}
|
||||
if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil {
|
||||
return newMount, err
|
||||
}
|
||||
newMount.Destination = kv[1]
|
||||
setDest = true
|
||||
default:
|
||||
return newMount, errors.Wrapf(errBadMntOption, kv[0])
|
||||
}
|
||||
}
|
||||
|
||||
if !setDest {
|
||||
return newMount, errBadVolDest
|
||||
}
|
||||
|
||||
return newMount, nil
|
||||
}
|
||||
|
||||
/* This is internal function and could be changed at any time */
|
||||
/* for external usage please refer to buildah/pkg/parse.GetTempDir() */
|
||||
func getTempDir() string {
|
||||
if tmpdir, ok := os.LookupEnv("TMPDIR"); ok {
|
||||
return tmpdir
|
||||
}
|
||||
return "/var/tmp"
|
||||
}
|
||||
|
||||
type StageMountDetails struct {
|
||||
IsStage bool // tells if mountpoint returned from stage executor is stage or image
|
||||
MountPoint string // mountpoint of stage/image
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
// Copyright © 2022 buildah.
|
||||
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 parse
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/containers/buildah/define"
|
||||
"github.com/containers/common/libimage"
|
||||
"github.com/containers/image/v5/types"
|
||||
"github.com/containers/storage"
|
||||
"github.com/containers/storage/pkg/archive"
|
||||
"github.com/containers/storage/pkg/chrootarchive"
|
||||
"github.com/containers/storage/pkg/unshare"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// LookupImage returns *Image to corresponding imagename or id
|
||||
func LookupImage(ctx *types.SystemContext, store storage.Store, image string) (*libimage.Image, error) {
|
||||
systemContext := ctx
|
||||
if systemContext == nil {
|
||||
systemContext = &types.SystemContext{}
|
||||
}
|
||||
runtime, err := libimage.RuntimeFromStore(store, &libimage.RuntimeOptions{SystemContext: systemContext})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
localImage, _, err := runtime.LookupImage(image, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return localImage, nil
|
||||
}
|
||||
|
||||
// ExportFromReader reads bytes from given reader and exports to external tar, directory or stdout.
|
||||
func ExportFromReader(input io.Reader, opts define.BuildOutputOption) error {
|
||||
var err error
|
||||
var path string
|
||||
if !filepath.IsAbs(opts.Path) {
|
||||
path, err = filepath.Abs(opts.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
opts.Path = path
|
||||
}
|
||||
if opts.IsDir {
|
||||
// In order to keep this feature as close as possible to
|
||||
// buildkit it was decided to preserve ownership when
|
||||
// invoked as root since caller already has access to artifacts
|
||||
// therefore we can preserve ownership as is, however for rootless users
|
||||
// ownership has to be changed so exported artifacts can still
|
||||
// be accessible by unpriviledged users.
|
||||
// See: https://github.com/containers/buildah/pull/3823#discussion_r829376633
|
||||
noLChown := false
|
||||
if unshare.IsRootless() {
|
||||
noLChown = true
|
||||
}
|
||||
|
||||
err = os.MkdirAll(opts.Path, 0700)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed while creating the destination path %q", opts.Path)
|
||||
}
|
||||
|
||||
err = chrootarchive.Untar(input, opts.Path, &archive.TarOptions{NoLchown: noLChown})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed while performing untar at %q", opts.Path)
|
||||
}
|
||||
} else {
|
||||
outFile := os.Stdout
|
||||
if !opts.IsStdout {
|
||||
outFile, err = os.Create(opts.Path)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed while creating destination tar at %q", opts.Path)
|
||||
}
|
||||
defer outFile.Close()
|
||||
}
|
||||
_, err = io.Copy(outFile, input)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed while performing copy to %q", opts.Path)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,895 +0,0 @@
|
||||
// Copyright © 2022 buildah.
|
||||
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 buildah
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
pp "github.com/labring/sealos/pkg/image/buildah/cluster/buildah/parse"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
|
||||
"github.com/containers/buildah"
|
||||
"github.com/containers/buildah/define"
|
||||
"github.com/containers/buildah/pkg/parse"
|
||||
"github.com/containers/buildah/util"
|
||||
"github.com/containers/common/libimage"
|
||||
"github.com/containers/common/pkg/config"
|
||||
"github.com/containers/common/pkg/umask"
|
||||
"github.com/containers/image/v5/manifest"
|
||||
is "github.com/containers/image/v5/storage"
|
||||
"github.com/containers/image/v5/transports"
|
||||
"github.com/containers/image/v5/transports/alltransports"
|
||||
ct "github.com/containers/image/v5/types"
|
||||
encconfig "github.com/containers/ocicrypt/config"
|
||||
enchelpers "github.com/containers/ocicrypt/helpers"
|
||||
"github.com/containers/storage"
|
||||
"github.com/containers/storage/pkg/idtools"
|
||||
"github.com/containers/storage/pkg/unshare"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func getStore(globalFlagResults *globalFlags) (storage.Store, error) {
|
||||
options, err := storage.DefaultStoreOptions(unshare.IsRootless(), unshare.GetRootlessUID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
options.GraphRoot = globalFlagResults.Root
|
||||
options.RunRoot = globalFlagResults.RunRoot
|
||||
|
||||
if err := setXDGRuntimeDir(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
options.GraphDriverName = globalFlagResults.StorageDriver
|
||||
// If any options setup in config, these should be dropped if user overrode the driver
|
||||
options.GraphDriverOptions = []string{}
|
||||
options.GraphDriverOptions = globalFlagResults.StorageOpts
|
||||
|
||||
// Do not allow to mount a graphdriver that is not vfs if we are creating the userns as part
|
||||
// of the mount command.
|
||||
// Differently, allow the mount if we are already in a userns, as the mount point will still
|
||||
// be accessible once "buildah mount" exits.
|
||||
|
||||
if os.Geteuid() != 0 && options.GraphDriverName != "vfs" {
|
||||
return nil, errors.Errorf("cannot mount using driver %s in rootless mode. You need to run it in a `buildah unshare` session", options.GraphDriverName)
|
||||
}
|
||||
|
||||
if len(globalFlagResults.UserNSUID) > 0 {
|
||||
uopts := globalFlagResults.UserNSUID
|
||||
gopts := globalFlagResults.UserNSGID
|
||||
|
||||
if len(gopts) == 0 {
|
||||
gopts = uopts
|
||||
}
|
||||
|
||||
uidmap, gidmap, err := unshare.ParseIDMappings(uopts, gopts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options.UIDMap = uidmap
|
||||
options.GIDMap = gidmap
|
||||
} else {
|
||||
if len(globalFlagResults.UserNSGID) > 0 {
|
||||
return nil, errors.New("option --userns-gid-map can not be used without --userns-uid-map")
|
||||
}
|
||||
}
|
||||
|
||||
// If a subcommand has the flags, check if they are set; if so, override the global values
|
||||
uopts := globalFlagResults.UserNSUID
|
||||
gopts := globalFlagResults.UserNSGID
|
||||
if len(gopts) == 0 {
|
||||
gopts = uopts
|
||||
}
|
||||
uidmap, gidmap, err := unshare.ParseIDMappings(uopts, gopts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options.UIDMap = uidmap
|
||||
options.GIDMap = gidmap
|
||||
|
||||
umask.Check()
|
||||
|
||||
store, err := storage.GetStore(options)
|
||||
if store != nil {
|
||||
is.Transport.SetStore(store)
|
||||
}
|
||||
return store, err
|
||||
}
|
||||
|
||||
func newGlobalOptions() *globalFlags {
|
||||
var (
|
||||
defaultStoreDriverOptions []string
|
||||
)
|
||||
storageOptions, err := storage.DefaultStoreOptions(false, 0)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
if len(storageOptions.GraphDriverOptions) > 0 {
|
||||
optionSlice := storageOptions.GraphDriverOptions[:]
|
||||
defaultStoreDriverOptions = optionSlice
|
||||
}
|
||||
containerConfig, err := config.Default()
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
containerConfig.CheckCgroupsAndAdjustConfig()
|
||||
return &globalFlags{
|
||||
Debug: true,
|
||||
LogLevel: "warn",
|
||||
Root: storageOptions.GraphRoot,
|
||||
RunRoot: storageOptions.RunRoot,
|
||||
StorageDriver: storageOptions.GraphDriverName,
|
||||
RegistriesConf: "",
|
||||
RegistriesConfDir: "",
|
||||
DefaultMountsFile: "",
|
||||
StorageOpts: defaultStoreDriverOptions,
|
||||
UserNSUID: []string{},
|
||||
UserNSGID: []string{},
|
||||
CPUProfile: "",
|
||||
MemoryProfile: "",
|
||||
UserShortNameAliasConfPath: "",
|
||||
CgroupManager: containerConfig.Engine.CgroupManager,
|
||||
}
|
||||
}
|
||||
|
||||
type globalFlags struct {
|
||||
Debug bool
|
||||
LogLevel string
|
||||
Root string
|
||||
RunRoot string
|
||||
StorageDriver string
|
||||
RegistriesConf string
|
||||
RegistriesConfDir string
|
||||
DefaultMountsFile string
|
||||
StorageOpts []string
|
||||
UserNSUID []string
|
||||
UserNSGID []string
|
||||
CPUProfile string
|
||||
//cpuProfileFile *os.File
|
||||
MemoryProfile string
|
||||
UserShortNameAliasConfPath string
|
||||
CgroupManager string
|
||||
}
|
||||
|
||||
func getGlobalOptionsFlag() *pflag.FlagSet {
|
||||
var defaultStoreDriverOptions []string
|
||||
|
||||
var globalFlagResults globalFlags
|
||||
fs := pflag.FlagSet{}
|
||||
storageOptions, err := storage.DefaultStoreOptions(false, 0)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if len(storageOptions.GraphDriverOptions) > 0 {
|
||||
optionSlice := storageOptions.GraphDriverOptions[:]
|
||||
defaultStoreDriverOptions = optionSlice
|
||||
}
|
||||
|
||||
containerConfig, err := config.Default()
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
containerConfig.CheckCgroupsAndAdjustConfig()
|
||||
|
||||
fs.BoolVar(&globalFlagResults.Debug, "debug", false, "print debugging information")
|
||||
// TODO Need to allow for environment variable
|
||||
fs.StringVar(&globalFlagResults.RegistriesConf, "registries-conf", "", "path to registries.conf file (not usually used)")
|
||||
fs.StringVar(&globalFlagResults.RegistriesConfDir, "registries-conf-dir", "", "path to registries.conf.d directory (not usually used)")
|
||||
fs.StringVar(&globalFlagResults.UserShortNameAliasConfPath, "short-name-alias-conf", "", "path to short name alias cache file (not usually used)")
|
||||
fs.StringVar(&globalFlagResults.Root, "root", storageOptions.GraphRoot, "storage root dir")
|
||||
fs.StringVar(&globalFlagResults.RunRoot, "runroot", storageOptions.RunRoot, "storage state dir")
|
||||
fs.StringVar(&globalFlagResults.CgroupManager, "cgroup-manager", containerConfig.Engine.CgroupManager, "cgroup manager")
|
||||
fs.StringVar(&globalFlagResults.StorageDriver, "storage-driver", storageOptions.GraphDriverName, "storage-driver")
|
||||
fs.StringSliceVar(&globalFlagResults.StorageOpts, "storage-opt", defaultStoreDriverOptions, "storage driver option")
|
||||
fs.StringSliceVar(&globalFlagResults.UserNSUID, "userns-uid-map", []string{}, "default `ctrID:hostID:length` UID mapping to use")
|
||||
fs.StringSliceVar(&globalFlagResults.UserNSGID, "userns-gid-map", []string{}, "default `ctrID:hostID:length` GID mapping to use")
|
||||
fs.StringVar(&globalFlagResults.DefaultMountsFile, "default-mounts-file", "", "path to default mounts file")
|
||||
fs.StringVar(&globalFlagResults.LogLevel, "logLevel", "warn", `The log level to be used. Either "trace", "debug", "info", "warn", "error", "fatal", or "panic".`)
|
||||
fs.StringVar(&globalFlagResults.CPUProfile, "cpu-profile", "", "`file` to write CPU profile")
|
||||
fs.StringVar(&globalFlagResults.MemoryProfile, "memory-profile", "", "`file` to write memory profile")
|
||||
|
||||
return &fs
|
||||
}
|
||||
|
||||
// setXDGRuntimeDir sets XDG_RUNTIME_DIR when if it is unset under rootless
|
||||
func setXDGRuntimeDir() error {
|
||||
if unshare.IsRootless() && os.Getenv("XDG_RUNTIME_DIR") == "" {
|
||||
runtimeDir, err := storage.GetRootlessRuntimeDir(unshare.GetRootlessUID())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Setenv("XDG_RUNTIME_DIR", runtimeDir); err != nil {
|
||||
return errors.New("could not set XDG_RUNTIME_DIR")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func openBuilders(store storage.Store) (builders []*buildah.Builder, err error) {
|
||||
return buildah.OpenAllBuilders(store)
|
||||
}
|
||||
|
||||
func openBuilder(ctx context.Context, store storage.Store, name string) (builder *buildah.Builder, err error) {
|
||||
if name != "" {
|
||||
builder, err = buildah.OpenBuilder(store, name)
|
||||
if os.IsNotExist(errors.Cause(err)) {
|
||||
options := buildah.ImportOptions{
|
||||
Container: name,
|
||||
}
|
||||
builder, err = buildah.ImportBuilder(ctx, store, options)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if builder == nil {
|
||||
return nil, errors.Errorf("error finding build container")
|
||||
}
|
||||
return builder, nil
|
||||
}
|
||||
|
||||
// getContext returns a context.TODO
|
||||
func getContext() context.Context {
|
||||
return context.TODO()
|
||||
}
|
||||
|
||||
func defaultFormat() string {
|
||||
format := os.Getenv("BUILDAH_FORMAT")
|
||||
if format != "" {
|
||||
return format
|
||||
}
|
||||
return buildah.OCI
|
||||
}
|
||||
|
||||
// pull push login
|
||||
// Currently, only TLS is set
|
||||
func getSystemContext(tls bool) (*ct.SystemContext, error) {
|
||||
certDir := ""
|
||||
ctx := &ct.SystemContext{
|
||||
DockerCertPath: certDir,
|
||||
}
|
||||
tlsVerify := tls
|
||||
ctx.DockerInsecureSkipTLSVerify = ct.NewOptionalBool(!tlsVerify)
|
||||
ctx.OCIInsecureSkipTLSVerify = !tlsVerify
|
||||
ctx.DockerDaemonInsecureSkipTLSVerify = !tlsVerify
|
||||
//
|
||||
ctx.OCIAcceptUncompressedLayers = true
|
||||
//
|
||||
//creds := opts.creds
|
||||
//
|
||||
//var err error
|
||||
//ctx.DockerAuthConfig, err = parse.AuthConfig(creds)
|
||||
//if err != nil {
|
||||
// return nil, err
|
||||
//}
|
||||
//
|
||||
//sigPolicy := opts.signaturePolicy
|
||||
//ctx.SignaturePolicyPath = sigPolicy
|
||||
//
|
||||
//authfile := opts.authfile
|
||||
//ctx.AuthFilePath = getAuthFile(authfile)
|
||||
//
|
||||
//regConf := ""
|
||||
//ctx.SystemRegistriesConfPath = regConf
|
||||
//
|
||||
//regConfDir := ""
|
||||
//ctx.RegistriesDirPath = regConfDir
|
||||
//
|
||||
//shortNameAliasConf := ""
|
||||
//ctx.UserShortNameAliasConfPath = shortNameAliasConf
|
||||
|
||||
ctx.DockerRegistryUserAgent = fmt.Sprintf("Buildah/%s", define.Version)
|
||||
|
||||
ctx.OSChoice = runtime.GOOS
|
||||
|
||||
ctx.ArchitectureChoice = runtime.GOARCH
|
||||
|
||||
ctx.VariantChoice = ""
|
||||
|
||||
ctx.BigFilesTemporaryDir = parse.GetTempDir()
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
// CommonBuildOptionsFromFlagSet parses the build options from the bud cli
|
||||
func CommonBuildOptionsFromFlagSet(flags *pflag.FlagSet) (*define.CommonBuildOptions, error) {
|
||||
var (
|
||||
memoryLimit int64
|
||||
memorySwap int64
|
||||
noDNS bool
|
||||
err error
|
||||
)
|
||||
defaultContainerConfig, err := config.Default()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get container config")
|
||||
}
|
||||
|
||||
memVal, _ := flags.GetString("memory")
|
||||
if memVal != "" {
|
||||
memoryLimit, err = units.RAMInBytes(memVal)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "invalid value for memory")
|
||||
}
|
||||
}
|
||||
|
||||
memSwapValue, _ := flags.GetString("memory-swap")
|
||||
if memSwapValue != "" {
|
||||
if memSwapValue == "-1" {
|
||||
memorySwap = -1
|
||||
} else {
|
||||
memorySwap, err = units.RAMInBytes(memSwapValue)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "invalid value for memory-swap")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
noHosts, _ := flags.GetBool("no-hosts")
|
||||
|
||||
addHost, _ := flags.GetStringSlice("add-host")
|
||||
if len(addHost) > 0 {
|
||||
if noHosts {
|
||||
return nil, errors.Errorf("--no-hosts and --add-host conflict, can not be used together")
|
||||
}
|
||||
for _, host := range addHost {
|
||||
if err := validateExtraHost(host); err != nil {
|
||||
return nil, errors.Wrapf(err, "invalid value for add-host")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
noDNS = false
|
||||
dnsServers := []string{}
|
||||
if flags.Changed("dns") {
|
||||
dnsServers, _ = flags.GetStringSlice("dns")
|
||||
for _, server := range dnsServers {
|
||||
if strings.ToLower(server) == "none" {
|
||||
noDNS = true
|
||||
}
|
||||
}
|
||||
if noDNS && len(dnsServers) > 1 {
|
||||
return nil, errors.Errorf("invalid --dns, --dns=none may not be used with any other --dns options")
|
||||
}
|
||||
}
|
||||
|
||||
dnsSearch := []string{}
|
||||
if flags.Changed("dns-search") {
|
||||
dnsSearch, _ = flags.GetStringSlice("dns-search")
|
||||
if noDNS && len(dnsSearch) > 0 {
|
||||
return nil, errors.Errorf("invalid --dns-search, --dns-search may not be used with --dns=none")
|
||||
}
|
||||
}
|
||||
|
||||
dnsOptions := []string{}
|
||||
if flags.Changed("dns-option") {
|
||||
dnsOptions, _ = flags.GetStringSlice("dns-option")
|
||||
if noDNS && len(dnsOptions) > 0 {
|
||||
return nil, errors.Errorf("invalid --dns-option, --dns-option may not be used with --dns=none")
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := units.FromHumanSize(defaultContainerConfig.Containers.ShmSize); err != nil {
|
||||
return nil, errors.Wrapf(err, "invalid --shm-size")
|
||||
}
|
||||
volumes, _ := flags.GetStringArray("volume")
|
||||
if err := Volumes(volumes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cpuPeriod, _ := flags.GetUint64("cpu-period")
|
||||
cpuQuota, _ := flags.GetInt64("cpu-quota")
|
||||
cpuShares, _ := flags.GetUint64("cpu-shares")
|
||||
httpProxy, _ := flags.GetBool("http-proxy")
|
||||
identityLabel, _ := flags.GetBool("identity-label")
|
||||
|
||||
ulimit := []string{}
|
||||
if flags.Changed("ulimit") {
|
||||
ulimit, _ = flags.GetStringSlice("ulimit")
|
||||
}
|
||||
|
||||
secrets, _ := flags.GetStringArray("secret")
|
||||
sshsources, _ := flags.GetStringArray("ssh")
|
||||
|
||||
cpuSetCPUs, _ := flags.GetString("cpuset-cpus")
|
||||
cpuSetMems, _ := flags.GetString("cpuset-mems")
|
||||
cgroupParent, _ := flags.GetString("cgroup-parent")
|
||||
shmSize := defaultContainerConfig.Containers.ShmSize
|
||||
|
||||
commonOpts := &define.CommonBuildOptions{
|
||||
AddHost: addHost,
|
||||
CPUPeriod: cpuPeriod,
|
||||
CPUQuota: cpuQuota,
|
||||
CPUSetCPUs: cpuSetCPUs,
|
||||
CPUSetMems: cpuSetMems,
|
||||
CPUShares: cpuShares,
|
||||
CgroupParent: cgroupParent,
|
||||
DNSOptions: dnsOptions,
|
||||
DNSSearch: dnsSearch,
|
||||
DNSServers: dnsServers,
|
||||
HTTPProxy: httpProxy,
|
||||
IdentityLabel: ct.NewOptionalBool(identityLabel),
|
||||
Memory: memoryLimit,
|
||||
MemorySwap: memorySwap,
|
||||
NoHosts: noHosts,
|
||||
ShmSize: shmSize,
|
||||
Ulimit: ulimit,
|
||||
Volumes: volumes,
|
||||
Secrets: secrets,
|
||||
SSHSources: sshsources,
|
||||
}
|
||||
securityOpts, _ := flags.GetStringArray("security-opt")
|
||||
if err := parseSecurityOpts(securityOpts, commonOpts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return commonOpts, nil
|
||||
}
|
||||
|
||||
// validateExtraHost validates that the specified string is a valid extrahost and returns it.
|
||||
// ExtraHost is in the form of name:ip where the ip has to be a valid ip (ipv4 or ipv6).
|
||||
// for add-host flag
|
||||
func validateExtraHost(val string) error {
|
||||
// allow for IPv6 addresses in extra hosts by only splitting on first ":"
|
||||
arr := strings.SplitN(val, ":", 2)
|
||||
if len(arr) != 2 || len(arr[0]) == 0 {
|
||||
return errors.Errorf("bad format for add-host: %q", val)
|
||||
}
|
||||
if _, err := validateIPAddress(arr[1]); err != nil {
|
||||
return errors.Errorf("invalid IP address in add-host: %q", arr[1])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseSecurityOpts(securityOpts []string, commonOpts *define.CommonBuildOptions) error {
|
||||
for _, opt := range securityOpts {
|
||||
if opt == "no-new-privileges" {
|
||||
return errors.Errorf("no-new-privileges is not supported")
|
||||
}
|
||||
con := strings.SplitN(opt, "=", 2)
|
||||
if len(con) != 2 {
|
||||
return errors.Errorf("invalid --security-opt name=value pair: %q", opt)
|
||||
}
|
||||
|
||||
switch con[0] {
|
||||
case "label":
|
||||
commonOpts.LabelOpts = append(commonOpts.LabelOpts, con[1])
|
||||
case "apparmor":
|
||||
commonOpts.ApparmorProfile = con[1]
|
||||
case "seccomp":
|
||||
commonOpts.SeccompProfilePath = con[1]
|
||||
default:
|
||||
return errors.Errorf("invalid --security-opt 2: %q", opt)
|
||||
}
|
||||
}
|
||||
|
||||
if commonOpts.SeccompProfilePath == "" {
|
||||
if _, err := os.Stat(SeccompOverridePath); err == nil {
|
||||
commonOpts.SeccompProfilePath = SeccompOverridePath
|
||||
} else {
|
||||
if !os.IsNotExist(err) {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
if _, err := os.Stat(SeccompDefaultPath); err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
} else {
|
||||
commonOpts.SeccompProfilePath = SeccompDefaultPath
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
// SeccompDefaultPath defines the default seccomp path
|
||||
SeccompDefaultPath = "/usr/share/containers/seccomp.json"
|
||||
// SeccompOverridePath if this exists it overrides the default seccomp path
|
||||
SeccompOverridePath = "/etc/crio/seccomp.json"
|
||||
// TypeBind is the type for mounting host dir
|
||||
TypeBind = "bind"
|
||||
// TypeTmpfs is the type for mounting tmpfs
|
||||
TypeTmpfs = "tmpfs"
|
||||
// TypeCache is the type for mounting a common persistent cache from host
|
||||
TypeCache = "cache"
|
||||
// mount=type=cache must create a persistent directory on host so its available for all consecutive builds.
|
||||
// Lifecycle of following directory will be inherited from how host machine treats temporary directory
|
||||
BuildahCacheDir = "buildah-cache"
|
||||
)
|
||||
|
||||
// validateIPAddress validates an Ip address.
|
||||
// for dns, ip, and ip6 flags also
|
||||
func validateIPAddress(val string) (string, error) {
|
||||
var ip = net.ParseIP(strings.TrimSpace(val))
|
||||
if ip != nil {
|
||||
return ip.String(), nil
|
||||
}
|
||||
return "", errors.Errorf("%s is not an ip address", val)
|
||||
}
|
||||
|
||||
// Volume parses the input of --volume
|
||||
func Volume(volume string) (specs.Mount, error) {
|
||||
return pp.Volume(volume)
|
||||
}
|
||||
|
||||
// Volumes validates the host and container paths passed in to the --volume flag
|
||||
func Volumes(volumes []string) error {
|
||||
if len(volumes) == 0 {
|
||||
return nil
|
||||
}
|
||||
for _, volume := range volumes {
|
||||
if _, err := Volume(volume); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NamespaceOptionsFromFlagSet parses the build options for all namespaces except for user namespace.
|
||||
func NamespaceOptions(flags *pflag.FlagSet) (namespaceOptions define.NamespaceOptions, networkPolicy define.NetworkConfigurationPolicy, err error) {
|
||||
options := make(define.NamespaceOptions, 0, 7)
|
||||
policy := define.NetworkDefault
|
||||
for _, what := range []string{"cgroupns", string(specs.IPCNamespace), "network", string(specs.PIDNamespace), string(specs.UTSNamespace)} {
|
||||
if flags.Lookup(what) != nil {
|
||||
how, _ := flags.GetString(what)
|
||||
switch what {
|
||||
case "cgroupns":
|
||||
what = string(specs.CgroupNamespace)
|
||||
}
|
||||
switch how {
|
||||
case "", "container", "private":
|
||||
logger.Debug("setting %q namespace to %q", what, "")
|
||||
policy = define.NetworkEnabled
|
||||
options.AddOrReplace(define.NamespaceOption{
|
||||
Name: what,
|
||||
})
|
||||
case "host":
|
||||
logger.Debug("setting %q namespace to host", what)
|
||||
policy = define.NetworkEnabled
|
||||
options.AddOrReplace(define.NamespaceOption{
|
||||
Name: what,
|
||||
Host: true,
|
||||
})
|
||||
default:
|
||||
if what == string(specs.NetworkNamespace) {
|
||||
if how == "none" {
|
||||
options.AddOrReplace(define.NamespaceOption{
|
||||
Name: what,
|
||||
})
|
||||
policy = define.NetworkDisabled
|
||||
logger.Debug("setting network to disabled")
|
||||
break
|
||||
}
|
||||
}
|
||||
how = strings.TrimPrefix(how, "ns:")
|
||||
// if not a path we assume it is a comma separated network list, see setupNamespaces() in run_linux.go
|
||||
if filepath.IsAbs(how) || what != string(specs.NetworkNamespace) {
|
||||
if _, err := os.Stat(how); err != nil {
|
||||
return nil, define.NetworkDefault, errors.Wrapf(err, "checking %s namespace", what)
|
||||
}
|
||||
}
|
||||
policy = define.NetworkEnabled
|
||||
logger.Debug("setting %q namespace to %q", what, how)
|
||||
options.AddOrReplace(define.NamespaceOption{
|
||||
Name: what,
|
||||
Path: how,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return options, policy, nil
|
||||
}
|
||||
|
||||
func getFormat(format string) (string, error) {
|
||||
switch format {
|
||||
case define.OCI:
|
||||
return define.OCIv1ImageManifest, nil
|
||||
case define.DOCKER:
|
||||
return define.Dockerv2ImageManifest, nil
|
||||
default:
|
||||
return "", errors.Errorf("unrecognized image type %q", format)
|
||||
}
|
||||
}
|
||||
|
||||
func IDMappingOptions(flags *pflag.FlagSet, persistentFlags *pflag.FlagSet) (usernsOptions define.NamespaceOptions, idmapOptions *define.IDMappingOptions, err error) {
|
||||
user, _ := flags.GetString("userns-uid-map-user")
|
||||
group, _ := flags.GetString("userns-gid-map-group")
|
||||
// If only the user or group was specified, use the same value for the
|
||||
// other, since we need both in order to initialize the maps using the
|
||||
// names.
|
||||
if user == "" && group != "" {
|
||||
user = group
|
||||
}
|
||||
if group == "" && user != "" {
|
||||
group = user
|
||||
}
|
||||
// Either start with empty maps or the name-based maps.
|
||||
mappings := idtools.NewIDMappingsFromMaps(nil, nil)
|
||||
if user != "" && group != "" {
|
||||
submappings, err := idtools.NewIDMappings(user, group)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
mappings = submappings
|
||||
}
|
||||
globalOptions := persistentFlags
|
||||
// We'll parse the UID and GID mapping options the same way.
|
||||
buildIDMap := func(basemap []idtools.IDMap, option string) ([]specs.LinuxIDMapping, error) {
|
||||
outmap := make([]specs.LinuxIDMapping, 0, len(basemap))
|
||||
// Start with the name-based map entries.
|
||||
for _, m := range basemap {
|
||||
outmap = append(outmap, specs.LinuxIDMapping{
|
||||
ContainerID: uint32(m.ContainerID),
|
||||
HostID: uint32(m.HostID),
|
||||
Size: uint32(m.Size),
|
||||
})
|
||||
}
|
||||
// Parse the flag's value as one or more triples (if it's even
|
||||
// been set), and append them.
|
||||
var spec []string
|
||||
if globalOptions.Lookup(option) != nil && globalOptions.Lookup(option).Changed {
|
||||
spec, _ = globalOptions.GetStringSlice(option)
|
||||
} else {
|
||||
spec, _ = flags.GetStringSlice(option)
|
||||
}
|
||||
|
||||
idmap, err := parseIDMap(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, m := range idmap {
|
||||
outmap = append(outmap, specs.LinuxIDMapping{
|
||||
ContainerID: m[0],
|
||||
HostID: m[1],
|
||||
Size: m[2],
|
||||
})
|
||||
}
|
||||
return outmap, nil
|
||||
}
|
||||
uidmap, err := buildIDMap(mappings.UIDs(), "userns-uid-map")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
gidmap, err := buildIDMap(mappings.GIDs(), "userns-gid-map")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
// If we only have one map or the other populated at this point, then
|
||||
// use the same mapping for both, since we know that no user or group
|
||||
// name was specified, but a specific mapping was for one or the other.
|
||||
if len(uidmap) == 0 && len(gidmap) != 0 {
|
||||
uidmap = gidmap
|
||||
}
|
||||
if len(gidmap) == 0 && len(uidmap) != 0 {
|
||||
gidmap = uidmap
|
||||
}
|
||||
|
||||
// By default, having mappings configured means we use a user
|
||||
// namespace. Otherwise, we don't.
|
||||
usernsOption := define.NamespaceOption{
|
||||
Name: string(specs.UserNamespace),
|
||||
Host: len(uidmap) == 0 && len(gidmap) == 0,
|
||||
}
|
||||
// If the user specifically requested that we either use or don't use
|
||||
// user namespaces, override that default.
|
||||
|
||||
how, _ := flags.GetString("userns")
|
||||
switch how {
|
||||
case "", "container", "private":
|
||||
usernsOption.Host = false
|
||||
case "host":
|
||||
usernsOption.Host = true
|
||||
default:
|
||||
how = strings.TrimPrefix(how, "ns:")
|
||||
if _, err := os.Stat(how); err != nil {
|
||||
return nil, nil, errors.Wrapf(err, "checking %s namespace", string(specs.UserNamespace))
|
||||
}
|
||||
logger.Debug("setting %q namespace to %q", string(specs.UserNamespace), how)
|
||||
usernsOption.Path = how
|
||||
}
|
||||
|
||||
usernsOptions = define.NamespaceOptions{usernsOption}
|
||||
|
||||
// If the user requested that we use the host namespace, but also that
|
||||
// we use mappings, that's not going to work.
|
||||
if (len(uidmap) != 0 || len(gidmap) != 0) && usernsOption.Host {
|
||||
return nil, nil, errors.Errorf("can not specify ID mappings while using host's user namespace")
|
||||
}
|
||||
return usernsOptions, &define.IDMappingOptions{
|
||||
HostUIDMapping: usernsOption.Host,
|
||||
HostGIDMapping: usernsOption.Host,
|
||||
UIDMap: uidmap,
|
||||
GIDMap: gidmap,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseIDMap(spec []string) (m [][3]uint32, err error) {
|
||||
for _, s := range spec {
|
||||
args := strings.FieldsFunc(s, func(r rune) bool { return !unicode.IsDigit(r) })
|
||||
if len(args)%3 != 0 {
|
||||
return nil, errors.Errorf("mapping %q is not in the form containerid:hostid:size[,...]", s)
|
||||
}
|
||||
for len(args) >= 3 {
|
||||
cid, err := strconv.ParseUint(args[0], 10, 32)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error parsing container ID %q from mapping %q as a number", args[0], s)
|
||||
}
|
||||
hostid, err := strconv.ParseUint(args[1], 10, 32)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error parsing host ID %q from mapping %q as a number", args[1], s)
|
||||
}
|
||||
size, err := strconv.ParseUint(args[2], 10, 32)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error parsing %q from mapping %q as a number", args[2], s)
|
||||
}
|
||||
m = append(m, [3]uint32{uint32(cid), uint32(hostid), uint32(size)})
|
||||
args = args[3:]
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func getDecryptConfig(decryptionKeys []string) (*encconfig.DecryptConfig, error) {
|
||||
decConfig := &encconfig.DecryptConfig{}
|
||||
if len(decryptionKeys) > 0 {
|
||||
// decryption
|
||||
dcc, err := enchelpers.CreateCryptoConfig([]string{}, decryptionKeys)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "invalid decryption keys")
|
||||
}
|
||||
cc := encconfig.CombineCryptoConfigs([]encconfig.CryptoConfig{dcc})
|
||||
decConfig = cc.DecryptConfig
|
||||
}
|
||||
|
||||
return decConfig, nil
|
||||
}
|
||||
|
||||
const (
|
||||
maxPullPushRetries = 3
|
||||
pullPushRetryDelay = 2 * time.Second
|
||||
)
|
||||
|
||||
func openImage(ctx context.Context, sc *ct.SystemContext, store storage.Store, name string) (builder *buildah.Builder, err error) {
|
||||
options := buildah.ImportFromImageOptions{
|
||||
Image: name,
|
||||
SystemContext: sc,
|
||||
}
|
||||
builder, err = buildah.ImportBuilderFromImage(ctx, store, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if builder == nil {
|
||||
return nil, errors.Errorf("error mocking up build configuration")
|
||||
}
|
||||
return builder, nil
|
||||
}
|
||||
|
||||
func manifestInspect(ctx context.Context, store storage.Store, systemContext *ct.SystemContext, imageSpec string) error {
|
||||
runtime, err := libimage.RuntimeFromStore(store, &libimage.RuntimeOptions{SystemContext: systemContext})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printManifest := func(manifest []byte) error {
|
||||
var b bytes.Buffer
|
||||
err = json.Indent(&b, manifest, "", " ")
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error rendering manifest for display")
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", b.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
// Before doing a remote lookup, attempt to resolve the manifest list
|
||||
// locally.
|
||||
manifestList, err := runtime.LookupManifestList(imageSpec)
|
||||
switch errors.Cause(err) {
|
||||
case storage.ErrImageUnknown, libimage.ErrNotAManifestList:
|
||||
// We need to do the remote inspection below.
|
||||
case nil:
|
||||
schema2List, err := manifestList.Inspect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rawSchema2List, err := json.Marshal(schema2List)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return printManifest(rawSchema2List)
|
||||
|
||||
default:
|
||||
// Fatal error.
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: at some point `libimage` should support resolving manifests
|
||||
// like that. Similar to `libimage.Runtime.LookupImage` we could
|
||||
// implement a `*.LookupImageIndex`.
|
||||
refs, err := util.ResolveNameToReferences(store, systemContext, imageSpec)
|
||||
if err != nil {
|
||||
logger.Debug("error parsing reference to image %q: %v", imageSpec, err)
|
||||
}
|
||||
|
||||
if ref, _, err := util.FindImage(store, "", systemContext, imageSpec); err == nil {
|
||||
refs = append(refs, ref)
|
||||
} else if ref, err := alltransports.ParseImageName(imageSpec); err == nil {
|
||||
refs = append(refs, ref)
|
||||
}
|
||||
if len(refs) == 0 {
|
||||
return errors.Errorf("error locating images with names %v", imageSpec)
|
||||
}
|
||||
|
||||
var (
|
||||
latestErr error
|
||||
result []byte
|
||||
)
|
||||
|
||||
appendErr := func(e error) {
|
||||
if latestErr == nil {
|
||||
latestErr = e
|
||||
} else {
|
||||
latestErr = errors.Wrapf(latestErr, "tried %v", e)
|
||||
}
|
||||
}
|
||||
|
||||
for _, ref := range refs {
|
||||
logger.Debug("Testing reference %q for possible manifest", transports.ImageName(ref))
|
||||
|
||||
src, err := ref.NewImageSource(ctx, systemContext)
|
||||
if err != nil {
|
||||
appendErr(errors.Wrapf(err, "reading image %q", transports.ImageName(ref)))
|
||||
continue
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
manifestBytes, manifestType, err := src.GetManifest(ctx, nil)
|
||||
if err != nil {
|
||||
appendErr(errors.Wrapf(err, "loading manifest %q", transports.ImageName(ref)))
|
||||
continue
|
||||
}
|
||||
|
||||
if !manifest.MIMETypeIsMultiImage(manifestType) {
|
||||
appendErr(errors.Errorf("manifest is of type %s (not a list type)", manifestType))
|
||||
continue
|
||||
}
|
||||
result = manifestBytes
|
||||
break
|
||||
}
|
||||
if len(result) == 0 && latestErr != nil {
|
||||
return latestErr
|
||||
}
|
||||
|
||||
return printManifest(result)
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
// Copyright © 2022 buildah.
|
||||
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 buildah
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/containers/buildah/util"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func RM(containerName string) error {
|
||||
delContainerErrStr := "error removing container"
|
||||
|
||||
globalFlagResults := newGlobalOptions()
|
||||
store, err := getStore(globalFlagResults)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var lastError error
|
||||
|
||||
for _, name := range []string{containerName} {
|
||||
builder, err := openBuilder(getContext(), store, name)
|
||||
if err != nil {
|
||||
lastError = util.WriteError(os.Stderr, errors.Wrapf(err, "%s %q", delContainerErrStr, name), lastError)
|
||||
continue
|
||||
}
|
||||
|
||||
if err = builder.Delete(); err != nil {
|
||||
lastError = util.WriteError(os.Stderr, errors.Wrapf(err, "%s %q", delContainerErrStr, name), lastError)
|
||||
continue
|
||||
}
|
||||
//fmt.Printf("%s\n", builder.ContainerID)
|
||||
}
|
||||
|
||||
return lastError
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
// Copyright © 2022 buildah.
|
||||
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 buildah
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Currently, only one Container is supported
|
||||
func UMount(containerName string) error {
|
||||
umountContainerErrStr := "error unmounting container"
|
||||
|
||||
globalFlagResults := newGlobalOptions()
|
||||
store, err := getStore(globalFlagResults)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var lastError error
|
||||
|
||||
for _, name := range []string{containerName} {
|
||||
builder, err := openBuilder(getContext(), store, name)
|
||||
if err != nil {
|
||||
if lastError != nil {
|
||||
fmt.Fprintln(os.Stderr, lastError)
|
||||
}
|
||||
lastError = errors.Wrapf(err, "%s %s", umountContainerErrStr, name)
|
||||
continue
|
||||
}
|
||||
if builder.MountPoint == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if err = builder.Unmount(); err != nil {
|
||||
if lastError != nil {
|
||||
fmt.Fprintln(os.Stderr, lastError)
|
||||
}
|
||||
lastError = errors.Wrapf(err, "%s %q", umountContainerErrStr, builder.Container)
|
||||
continue
|
||||
}
|
||||
//fmt.Printf("%s\n", builder.ContainerID)
|
||||
}
|
||||
|
||||
return lastError
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
Copyright 2022 sealos.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cluster
|
||||
|
||||
import "github.com/labring/sealos/pkg/image/types"
|
||||
|
||||
type Service struct {
|
||||
}
|
||||
|
||||
func NewClusterService() (types.ClusterService, error) {
|
||||
return &Service{}, nil
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
Copyright 2022 sealos.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cluster
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
bb "github.com/labring/sealos/pkg/image/buildah/cluster/buildah"
|
||||
"github.com/labring/sealos/pkg/image/types"
|
||||
)
|
||||
|
||||
func (c *Service) Create(name string, image string) (*types.ClusterManifest, error) {
|
||||
// delete
|
||||
err := c.Delete(name)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "create delete container fail")
|
||||
}
|
||||
// from
|
||||
err = bb.From(name, image)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "create from fail")
|
||||
}
|
||||
// mount
|
||||
err = bb.Mount(name)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "create mount fail")
|
||||
}
|
||||
|
||||
return c.Inspect(name)
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
Copyright 2022 sealos.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cluster
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
|
||||
bb "github.com/labring/sealos/pkg/image/buildah/cluster/buildah"
|
||||
)
|
||||
|
||||
func (c *Service) Delete(name string) error {
|
||||
infos, err := c.List()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "delete get containers")
|
||||
}
|
||||
logger.Debug("current container names is: %v", name)
|
||||
for _, info := range infos {
|
||||
if info.Containername == name {
|
||||
// umount
|
||||
err := bb.UMount(name)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "when delete container unmount fail")
|
||||
}
|
||||
// rm
|
||||
err = bb.RM(name)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "when delete container rm fail")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
Copyright 2022 sealos.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package cluster
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
bb "github.com/labring/sealos/pkg/image/buildah/cluster/buildah"
|
||||
"github.com/labring/sealos/pkg/image/types"
|
||||
)
|
||||
|
||||
func (*Service) Inspect(name string) (*types.ClusterManifest, error) {
|
||||
out, err := bb.Inspect(name)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "inspect get data fail")
|
||||
}
|
||||
manifest := &types.ClusterManifest{
|
||||
Container: out.Container,
|
||||
ContainerID: out.ContainerID,
|
||||
MountPoint: out.MountPoint,
|
||||
}
|
||||
return manifest, nil
|
||||
}
|
||||
|
||||
//func inspectContainer(data string) (*types.ClusterManifest, error) {
|
||||
// if data != "" {
|
||||
// var outStruct map[string]interface{}
|
||||
// err := json.Unmarshal([]byte(data), &outStruct)
|
||||
// if err != nil {
|
||||
// return nil, errors.Wrap(err, "decode out json from container inspect failed")
|
||||
// }
|
||||
// container, _, _ := unstructured.NestedString(outStruct, "Container")
|
||||
// containerID, _, _ := unstructured.NestedString(outStruct, "ContainerID")
|
||||
// mountPoint, _, _ := unstructured.NestedString(outStruct, "MountPoint")
|
||||
// manifest := &types.ClusterManifest{
|
||||
// Container: container,
|
||||
// ContainerID: containerID,
|
||||
// MountPoint: mountPoint,
|
||||
// }
|
||||
// return manifest, nil
|
||||
// }
|
||||
// return nil, errors.New("inspect output is empty")
|
||||
//}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
Copyright 2022 sealos.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package cluster
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
bb "github.com/labring/sealos/pkg/image/buildah/cluster/buildah"
|
||||
"github.com/labring/sealos/pkg/image/types"
|
||||
)
|
||||
|
||||
func (*Service) List() ([]types.ClusterInfo, error) {
|
||||
data, err := bb.GetContainers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
infos, err := listContainer(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return infos, nil
|
||||
}
|
||||
|
||||
func listContainer(data []byte) ([]types.ClusterInfo, error) {
|
||||
if string(data) != "" {
|
||||
var outStruct []types.ClusterInfo
|
||||
err := json.Unmarshal(data, &outStruct)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "decode out json from list container failed")
|
||||
}
|
||||
return outStruct, nil
|
||||
}
|
||||
return nil, errors.New("containers output is empty")
|
||||
}
|
||||
@@ -1,279 +0,0 @@
|
||||
// Copyright © 2022 buildah.
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 image
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/labring/sealos/pkg/constants"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
|
||||
"github.com/containers/buildah/define"
|
||||
"github.com/containers/buildah/imagebuildah"
|
||||
buildahcli "github.com/containers/buildah/pkg/cli"
|
||||
"github.com/containers/buildah/pkg/parse"
|
||||
buildahutil "github.com/containers/buildah/pkg/util"
|
||||
"github.com/containers/common/pkg/auth"
|
||||
image_types "github.com/containers/image/v5/types"
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/labring/sealos/pkg/buildimage"
|
||||
"github.com/labring/sealos/pkg/image/types"
|
||||
"github.com/labring/sealos/pkg/registry"
|
||||
)
|
||||
|
||||
func (d *Service) Build(options *types.BuildOptions, contextDir, imageName string) error {
|
||||
images, err := buildimage.List(contextDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
auths, err := registry.GetAuthInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
is := registry.NewImageSaver(context.Background(), options.MaxPullProcs, auths)
|
||||
platform := strings.Split(options.Platform, "/")
|
||||
var platformVar v1.Platform
|
||||
if len(platform) > 2 {
|
||||
platformVar = v1.Platform{
|
||||
Architecture: platform[1],
|
||||
OS: platform[0],
|
||||
Variant: platform[2],
|
||||
}
|
||||
} else {
|
||||
platformVar = v1.Platform{
|
||||
Architecture: platform[1],
|
||||
OS: platform[0],
|
||||
}
|
||||
}
|
||||
logger.Info("pull images %v for platform is %s", images, strings.Join([]string{platformVar.OS, platformVar.Architecture}, "/"))
|
||||
if options.SaveImage {
|
||||
images, err = is.SaveImages(images, path.Join(contextDir, constants.RegistryDirName), platformVar)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "save images failed in this context")
|
||||
}
|
||||
logger.Info("output images %v for platform is %s", images, strings.Join([]string{platformVar.OS, platformVar.Architecture}, "/"))
|
||||
} else {
|
||||
logger.Warn("current saveImage=false, skip pull images")
|
||||
}
|
||||
options.Tag = imageName
|
||||
// start call buildah build
|
||||
output := options.Tag
|
||||
cleanTmpFile := false
|
||||
var tags []string
|
||||
|
||||
if err = auth.CheckAuthFile(d.buildahBuildOptions.BudResults.Authfile); err != nil {
|
||||
return err
|
||||
}
|
||||
iopts := d.buildahBuildOptions
|
||||
d.buildahBuildOptions.BudResults.Authfile, cleanTmpFile = buildahutil.MirrorToTempFileIfPathIsDescriptor(iopts.BudResults.Authfile)
|
||||
if cleanTmpFile {
|
||||
defer os.Remove(d.buildahBuildOptions.BudResults.Authfile)
|
||||
}
|
||||
// will not be documented.
|
||||
pullPolicy := define.PullIfMissing
|
||||
if strings.EqualFold(strings.TrimSpace(iopts.Pull), "true") {
|
||||
pullPolicy = define.PullIfNewer
|
||||
}
|
||||
if iopts.PullAlways || strings.EqualFold(strings.TrimSpace(iopts.Pull), "always") {
|
||||
pullPolicy = define.PullAlways
|
||||
}
|
||||
if iopts.PullNever || strings.EqualFold(strings.TrimSpace(iopts.Pull), "never") {
|
||||
pullPolicy = define.PullNever
|
||||
}
|
||||
logger.Debug("Pull Policy for pull [%v]", pullPolicy)
|
||||
|
||||
args := make(map[string]string)
|
||||
for _, arg := range iopts.BuildArg {
|
||||
av := strings.SplitN(arg, "=", 2)
|
||||
if len(av) > 1 {
|
||||
args[av[0]] = av[1]
|
||||
} else {
|
||||
// check if the env is set in the local environment and use that value if it is
|
||||
if val, present := os.LookupEnv(av[0]); present {
|
||||
args[av[0]] = val
|
||||
} else {
|
||||
delete(args, av[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
containerfiles := getContainerfiles([]string{options.File})
|
||||
format, err := getFormat(iopts.Format)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
layers := buildahcli.UseLayers()
|
||||
contextDir, err = GetContextDir(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(containerfiles) == 0 {
|
||||
// Try to find the Containerfile/Dockerfile within the contextDir
|
||||
containerfile, err := buildahutil.DiscoverContainerfile(contextDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
containerfiles = append(containerfiles, containerfile)
|
||||
contextDir = filepath.Dir(containerfile)
|
||||
}
|
||||
contextDir, err = filepath.EvalSymlinks(contextDir)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error evaluating symlinks in build context path")
|
||||
}
|
||||
|
||||
var stdin io.Reader
|
||||
if iopts.Stdin {
|
||||
stdin = os.Stdin
|
||||
}
|
||||
var stdout, stderr, reporter *os.File
|
||||
stdout = os.Stdout
|
||||
stderr = os.Stderr
|
||||
reporter = os.Stderr
|
||||
|
||||
store := *d.store
|
||||
systemContext := &image_types.SystemContext{}
|
||||
|
||||
isolation, err := parse.IsolationOption(iopts.Isolation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
runtimeFlags := []string{}
|
||||
for _, arg := range iopts.RuntimeFlags {
|
||||
runtimeFlags = append(runtimeFlags, "--"+arg)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
compression := define.Gzip
|
||||
if iopts.DisableCompression {
|
||||
compression = define.Uncompressed
|
||||
}
|
||||
buildOption, err := parse.GetBuildOutput(iopts.BuildOutput)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if buildOption.IsStdout {
|
||||
iopts.Quiet = true
|
||||
}
|
||||
|
||||
var timestamp *time.Time
|
||||
t := time.Now().UTC()
|
||||
timestamp = &t
|
||||
var platforms []struct{ OS, Arch, Variant string }
|
||||
platforms = append(platforms, struct{ OS, Arch, Variant string }{platformVar.OS, platformVar.Architecture, platformVar.Variant})
|
||||
buildahOptions := define.BuildOptions{
|
||||
AddCapabilities: iopts.CapAdd,
|
||||
AdditionalTags: tags,
|
||||
AllPlatforms: iopts.AllPlatforms,
|
||||
Annotations: iopts.Annotation,
|
||||
Architecture: systemContext.ArchitectureChoice,
|
||||
Args: args,
|
||||
BlobDirectory: iopts.BlobCache,
|
||||
CNIConfigDir: iopts.CNIConfigDir,
|
||||
CNIPluginPath: iopts.CNIPlugInPath,
|
||||
Compression: compression,
|
||||
ContextDirectory: contextDir,
|
||||
DefaultMountsFilePath: d.globalFlagResults.DefaultMountsFile,
|
||||
Devices: iopts.Devices,
|
||||
DropCapabilities: iopts.CapDrop,
|
||||
Err: stderr,
|
||||
ForceRmIntermediateCtrs: iopts.ForceRm,
|
||||
From: iopts.From,
|
||||
IIDFile: iopts.Iidfile,
|
||||
In: stdin,
|
||||
Isolation: isolation,
|
||||
IgnoreFile: iopts.IgnoreFile,
|
||||
Labels: iopts.Label,
|
||||
Layers: layers,
|
||||
LogRusage: iopts.LogRusage,
|
||||
Manifest: iopts.Manifest,
|
||||
MaxPullPushRetries: maxPullPushRetries,
|
||||
NoCache: iopts.NoCache,
|
||||
OS: systemContext.OSChoice,
|
||||
Out: stdout,
|
||||
Output: output,
|
||||
BuildOutput: iopts.BuildOutput,
|
||||
OutputFormat: format,
|
||||
PullPolicy: pullPolicy,
|
||||
PullPushRetryDelay: pullPushRetryDelay,
|
||||
Quiet: iopts.Quiet,
|
||||
RemoveIntermediateCtrs: iopts.Rm,
|
||||
ReportWriter: reporter,
|
||||
Runtime: iopts.Runtime,
|
||||
RuntimeArgs: runtimeFlags,
|
||||
RusageLogFile: iopts.RusageLogFile,
|
||||
SignBy: iopts.SignBy,
|
||||
SignaturePolicyPath: iopts.SignaturePolicy,
|
||||
Squash: iopts.Squash,
|
||||
SystemContext: systemContext,
|
||||
Target: iopts.Target,
|
||||
TransientMounts: iopts.Volumes,
|
||||
Jobs: &iopts.Jobs,
|
||||
Timestamp: timestamp,
|
||||
Platforms: platforms,
|
||||
UnsetEnvs: iopts.UnsetEnvs,
|
||||
Envs: iopts.Envs,
|
||||
OSFeatures: iopts.OSFeatures,
|
||||
OSVersion: iopts.OSVersion,
|
||||
}
|
||||
if iopts.Quiet {
|
||||
buildahOptions.ReportWriter = io.Discard
|
||||
}
|
||||
|
||||
id, ref, err := imagebuildah.BuildDockerfiles(getContext(), store, buildahOptions, containerfiles...)
|
||||
if err == nil && buildahOptions.Manifest != "" {
|
||||
logger.Debug("manifest list id = %q, ref = %q", id, ref.String())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func getContainerfiles(files []string) []string {
|
||||
var containerfiles []string
|
||||
for _, f := range files {
|
||||
if f == "-" {
|
||||
containerfiles = append(containerfiles, "/dev/stdin")
|
||||
} else {
|
||||
containerfiles = append(containerfiles, f)
|
||||
}
|
||||
}
|
||||
return containerfiles
|
||||
}
|
||||
|
||||
func newBuildahBuildOptions() *types.BuildahBuildOptions {
|
||||
layerFlagsResults := buildahcli.LayerResults{}
|
||||
buildFlagResults := buildahcli.BudResults{}
|
||||
buildFlagResults.Format = define.OCI
|
||||
|
||||
fromAndBudResults := buildahcli.FromAndBudResults{}
|
||||
userNSResults := buildahcli.UserNSResults{}
|
||||
namespaceResults := buildahcli.NameSpaceResults{}
|
||||
return &types.BuildahBuildOptions{
|
||||
LayerResults: &layerFlagsResults,
|
||||
BudResults: &buildFlagResults,
|
||||
UserNSResults: &userNSResults,
|
||||
FromAndBudResults: &fromAndBudResults,
|
||||
NameSpaceResults: &namespaceResults,
|
||||
}
|
||||
}
|
||||
@@ -1,318 +0,0 @@
|
||||
// Copyright © 2022 buildah.
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 image
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
|
||||
"github.com/containers/buildah"
|
||||
"github.com/containers/buildah/define"
|
||||
"github.com/containers/buildah/pkg/formats"
|
||||
"github.com/containers/common/libimage"
|
||||
"github.com/containers/image/v5/types"
|
||||
encconfig "github.com/containers/ocicrypt/config"
|
||||
enchelpers "github.com/containers/ocicrypt/helpers"
|
||||
"github.com/containers/storage"
|
||||
"github.com/containers/storage/pkg/unshare"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
labring_types "github.com/labring/sealos/pkg/image/types"
|
||||
)
|
||||
|
||||
const (
|
||||
maxPullPushRetries = 3
|
||||
pullPushRetryDelay = 2 * time.Second
|
||||
)
|
||||
|
||||
// setXDGRuntimeDir sets XDG_RUNTIME_DIR when if it is unset under rootless
|
||||
func setXDGRuntimeDir() error {
|
||||
if unshare.IsRootless() && os.Getenv("XDG_RUNTIME_DIR") == "" {
|
||||
runtimeDir, err := storage.GetRootlessRuntimeDir(unshare.GetRootlessUID())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Setenv("XDG_RUNTIME_DIR", runtimeDir); err != nil {
|
||||
return errors.New("could not set XDG_RUNTIME_DIR")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func openBuilder(ctx context.Context, store storage.Store, name string) (builder *buildah.Builder, err error) {
|
||||
if name != "" {
|
||||
builder, err = buildah.OpenBuilder(store, name)
|
||||
if os.IsNotExist(errors.Cause(err)) {
|
||||
options := buildah.ImportOptions{
|
||||
Container: name,
|
||||
}
|
||||
builder, err = buildah.ImportBuilder(ctx, store, options)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if builder == nil {
|
||||
return nil, errors.Errorf("error finding build container")
|
||||
}
|
||||
return builder, nil
|
||||
}
|
||||
|
||||
func openImage(ctx context.Context, sc *types.SystemContext, store storage.Store, name string) (builder *buildah.Builder, err error) {
|
||||
options := buildah.ImportFromImageOptions{
|
||||
Image: name,
|
||||
SystemContext: sc,
|
||||
}
|
||||
builder, err = buildah.ImportBuilderFromImage(ctx, store, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if builder == nil {
|
||||
return nil, errors.Errorf("error mocking up build configuration")
|
||||
}
|
||||
return builder, nil
|
||||
}
|
||||
|
||||
// getContext returns a context.TODO
|
||||
func getContext() context.Context {
|
||||
return context.TODO()
|
||||
}
|
||||
|
||||
func getFormat(format string) (string, error) {
|
||||
switch format {
|
||||
case define.OCI:
|
||||
return define.OCIv1ImageManifest, nil
|
||||
case define.DOCKER:
|
||||
return define.Dockerv2ImageManifest, nil
|
||||
default:
|
||||
return "", errors.Errorf("unrecognized image type %q", format)
|
||||
}
|
||||
}
|
||||
|
||||
func getDecryptConfig(decryptionKeys []string) (*encconfig.DecryptConfig, error) {
|
||||
decConfig := &encconfig.DecryptConfig{}
|
||||
if len(decryptionKeys) > 0 {
|
||||
// decryption
|
||||
dcc, err := enchelpers.CreateCryptoConfig([]string{}, decryptionKeys)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "invalid decryption keys")
|
||||
}
|
||||
cc := encconfig.CombineCryptoConfigs([]encconfig.CryptoConfig{dcc})
|
||||
decConfig = cc.DecryptConfig
|
||||
}
|
||||
|
||||
return decConfig, nil
|
||||
}
|
||||
|
||||
func getEncryptConfig(encryptionKeys []string, encryptLayers []int) (*encconfig.EncryptConfig, *[]int, error) {
|
||||
var encLayers *[]int
|
||||
var encConfig *encconfig.EncryptConfig
|
||||
|
||||
if len(encryptionKeys) > 0 {
|
||||
// encryption
|
||||
encLayers = &encryptLayers
|
||||
ecc, err := enchelpers.CreateCryptoConfig(encryptionKeys, []string{})
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrapf(err, "invalid encryption keys")
|
||||
}
|
||||
cc := encconfig.CombineCryptoConfigs([]encconfig.CryptoConfig{ecc})
|
||||
encConfig = cc.EncryptConfig
|
||||
}
|
||||
return encConfig, encLayers, nil
|
||||
}
|
||||
|
||||
// Tail returns a string slice after the first element unless there are
|
||||
// not enough elements, then it returns an empty slice. This is to replace
|
||||
// the urfavecli Tail method for args
|
||||
func Tail(a []string) []string {
|
||||
if len(a) >= 2 {
|
||||
return a[1:]
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// UsageTemplate returns the usage template for podman commands
|
||||
// This blocks the displaying of the global options. The main podman
|
||||
// command should not use this.
|
||||
func UsageTemplate() string {
|
||||
return `Usage:{{if .Runnable}}
|
||||
{{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
|
||||
{{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
|
||||
|
||||
Aliases:
|
||||
{{.NameAndAliases}}{{end}}{{if .HasExample}}
|
||||
|
||||
Examples:
|
||||
{{.Example}}{{end}}{{if .HasAvailableSubCommands}}
|
||||
|
||||
Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
|
||||
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
|
||||
|
||||
Flags:
|
||||
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
|
||||
{{end}}
|
||||
`
|
||||
}
|
||||
|
||||
func imagesToGeneric(templParams []labring_types.ImageOutputParams) (genericParams []interface{}) {
|
||||
if len(templParams) > 0 {
|
||||
for _, v := range templParams {
|
||||
genericParams = append(genericParams, interface{}(v))
|
||||
}
|
||||
}
|
||||
return genericParams
|
||||
}
|
||||
|
||||
func outputHeader(opts labring_types.ImageOptions) string {
|
||||
if opts.Format != "" {
|
||||
return strings.Replace(opts.Format, `\t`, "\t", -1)
|
||||
}
|
||||
if opts.Quiet {
|
||||
return formats.IDString
|
||||
}
|
||||
format := "table {{.Name}}\t{{.Tag}}\t"
|
||||
if opts.NoHeading {
|
||||
format = "{{.Name}}\t{{.Tag}}\t"
|
||||
}
|
||||
|
||||
if opts.Digests {
|
||||
format += "{{.Digest}}\t"
|
||||
}
|
||||
format += "{{.ID}}\t{{.CreatedAt}}\t{{.Size}}"
|
||||
if opts.ReadOnly {
|
||||
format += "\t{{.ReadOnly}}"
|
||||
}
|
||||
if opts.History {
|
||||
format += "\t{{.History}}"
|
||||
}
|
||||
return format
|
||||
}
|
||||
|
||||
func formatHistory(history []string, name, tag string) string {
|
||||
if len(history) == 0 {
|
||||
return none
|
||||
}
|
||||
// Skip the first history entry if already existing as name
|
||||
if fmt.Sprintf("%s:%s", name, tag) == history[0] {
|
||||
if len(history) == 1 {
|
||||
return none
|
||||
}
|
||||
return strings.Join(history[1:], ", ")
|
||||
}
|
||||
return strings.Join(history, ", ")
|
||||
}
|
||||
|
||||
func formatImagesJSON(images []*libimage.Image, opts labring_types.ImageOptions) error {
|
||||
jsonImages := []labring_types.JSONImage{}
|
||||
for _, img := range images {
|
||||
// Copy the base data over to the output param.
|
||||
size, err := img.Size()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
created := img.Created()
|
||||
jsonImages = append(jsonImages,
|
||||
labring_types.JSONImage{
|
||||
CreatedAtRaw: created,
|
||||
Created: created.Unix(),
|
||||
CreatedAt: units.HumanDuration(time.Since(created)) + " ago",
|
||||
Digest: img.Digest().String(),
|
||||
ID: truncateID(img.ID(), opts.Truncate),
|
||||
Names: img.Names(),
|
||||
ReadOnly: img.IsReadOnly(),
|
||||
Size: formattedSize(size),
|
||||
})
|
||||
}
|
||||
|
||||
data, err := json.MarshalIndent(jsonImages, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%s\n", data)
|
||||
return nil
|
||||
}
|
||||
|
||||
const idTruncLength = 12
|
||||
|
||||
func truncateID(id string, truncate bool) string {
|
||||
if !truncate {
|
||||
return "sha256:" + id
|
||||
}
|
||||
if len(id) > idTruncLength {
|
||||
return id[:idTruncLength]
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
func formattedSize(size int64) string {
|
||||
suffixes := [5]string{"B", "KB", "MB", "GB", "TB"}
|
||||
|
||||
count := 0
|
||||
formattedSize := float64(size)
|
||||
for formattedSize >= 1000 && count < 4 {
|
||||
formattedSize /= 1000
|
||||
count++
|
||||
}
|
||||
return fmt.Sprintf("%.3g %s", formattedSize, suffixes[count])
|
||||
}
|
||||
|
||||
func GetContextDir(options *labring_types.BuildOptions) (string, error) {
|
||||
//fmt.Sprintf("buildah build %s %s", options.String(), contextDir)
|
||||
cliArgs := strings.Split(options.String(), " ")
|
||||
// Nothing provided, we assume the current working directory as build
|
||||
// context
|
||||
var contextDir string
|
||||
var err error
|
||||
if len(cliArgs) == 0 {
|
||||
contextDir, err = os.Getwd()
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "unable to choose current working directory as build context")
|
||||
}
|
||||
return contextDir, err
|
||||
}
|
||||
// The context directory could be a URL. Try to handle that.
|
||||
tempDir, subDir, err := define.TempDirForURL("", "buildah", cliArgs[0])
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "error prepping temporary context directory")
|
||||
return contextDir, err
|
||||
}
|
||||
if tempDir != "" {
|
||||
// We had to download it to a temporary directory.
|
||||
// Delete it later.
|
||||
defer func() {
|
||||
if err = os.RemoveAll(tempDir); err != nil {
|
||||
logger.Error("error removing temporary directory: %v", err)
|
||||
}
|
||||
}()
|
||||
contextDir = filepath.Join(tempDir, subDir)
|
||||
} else {
|
||||
// Nope, it was local. Use it as is.
|
||||
absDir, err := filepath.Abs(cliArgs[0])
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "error determining path to directory")
|
||||
return contextDir, err
|
||||
}
|
||||
contextDir = absDir
|
||||
}
|
||||
return contextDir, err
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
/*
|
||||
Copyright 2022 cuisongliu@qq.com.
|
||||
|
||||
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 image
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/containers/buildah/pkg/formats"
|
||||
"github.com/containers/common/libimage"
|
||||
image_types "github.com/containers/image/v5/types"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/labring/sealos/pkg/image/types"
|
||||
)
|
||||
|
||||
const none = "<none>"
|
||||
|
||||
var imagesHeader = map[string]string{
|
||||
"Name": "REPOSITORY",
|
||||
"Tag": "TAG",
|
||||
"ID": "IMAGE ID",
|
||||
"CreatedAt": "CREATED",
|
||||
"Size": "SIZE",
|
||||
"ReadOnly": "R/O",
|
||||
"History": "HISTORY",
|
||||
}
|
||||
|
||||
func (d *Service) ListImages() error {
|
||||
store := *d.store
|
||||
systemContext := &image_types.SystemContext{}
|
||||
|
||||
runtime, err := libimage.RuntimeFromStore(store, &libimage.RuntimeOptions{SystemContext: systemContext})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
iopts := d.listImageOpts
|
||||
ctx := context.Background()
|
||||
options := &libimage.ListImagesOptions{}
|
||||
if len(iopts.Filter) > 0 {
|
||||
options.Filters = iopts.Filter
|
||||
}
|
||||
if !iopts.All {
|
||||
options.Filters = append(options.Filters, "intermediate=false")
|
||||
}
|
||||
|
||||
images, err := runtime.ListImages(ctx, d.listImageOpts.Names, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if iopts.Quiet && iopts.Format != "" {
|
||||
return errors.Errorf("quiet and format are mutually exclusive")
|
||||
}
|
||||
|
||||
opts := types.ImageOptions{
|
||||
All: iopts.All,
|
||||
Digests: iopts.Digests,
|
||||
Format: iopts.Format,
|
||||
JSON: iopts.JSON,
|
||||
NoHeading: iopts.NoHeading,
|
||||
Truncate: !iopts.Truncate,
|
||||
Quiet: iopts.Quiet,
|
||||
History: iopts.History,
|
||||
}
|
||||
|
||||
if opts.JSON {
|
||||
return formatImagesJSON(images, opts)
|
||||
}
|
||||
|
||||
return formatImages(images, opts)
|
||||
}
|
||||
|
||||
type imagesSorted []types.ImageOutputParams
|
||||
|
||||
func (a imagesSorted) Less(i, j int) bool {
|
||||
return a[i].CreatedAtRaw.After(a[j].CreatedAtRaw)
|
||||
}
|
||||
|
||||
func (a imagesSorted) Len() int {
|
||||
return len(a)
|
||||
}
|
||||
|
||||
func (a imagesSorted) Swap(i, j int) {
|
||||
a[i], a[j] = a[j], a[i]
|
||||
}
|
||||
|
||||
func formatImages(images []*libimage.Image, opts types.ImageOptions) error {
|
||||
var outputData imagesSorted
|
||||
|
||||
for _, img := range images {
|
||||
var outputParam types.ImageOutputParams
|
||||
size, err := img.Size()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
created := img.Created()
|
||||
outputParam.CreatedAtRaw = created
|
||||
outputParam.Created = created.Unix()
|
||||
outputParam.CreatedAt = units.HumanDuration(time.Since(created)) + " ago"
|
||||
outputParam.Digest = img.Digest().String()
|
||||
outputParam.ID = truncateID(img.ID(), opts.Truncate)
|
||||
outputParam.Size = formattedSize(size)
|
||||
outputParam.ReadOnly = img.IsReadOnly()
|
||||
|
||||
repoTags, err := img.NamedRepoTags()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nameTagPairs, err := libimage.ToNameTagPairs(repoTags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, pair := range nameTagPairs {
|
||||
newParam := outputParam
|
||||
newParam.Name = pair.Name
|
||||
newParam.Tag = pair.Tag
|
||||
newParam.History = formatHistory(img.NamesHistory(), pair.Name, pair.Tag)
|
||||
outputData = append(outputData, newParam)
|
||||
// `images -q` should a given ID only once.
|
||||
if opts.Quiet {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(outputData)
|
||||
out := formats.StdoutTemplateArray{Output: imagesToGeneric(outputData), Template: outputHeader(opts), Fields: imagesHeader}
|
||||
return formats.Writer(out).Out()
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
// Copyright © 2022 buildah.
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 image
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
json2 "github.com/labring/sealos/pkg/utils/json"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
|
||||
"github.com/containers/buildah"
|
||||
image_types "github.com/containers/image/v5/types"
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
"github.com/labring/sealos/pkg/image/types"
|
||||
)
|
||||
|
||||
const (
|
||||
inspectTypeContainer = "container"
|
||||
inspectTypeImage = "image"
|
||||
inspectTypeManifest = "manifest"
|
||||
)
|
||||
|
||||
func (d *Service) Inspect(images ...string) (types.ImageListOCIV1, error) {
|
||||
var builder *buildah.Builder
|
||||
var imageList types.ImageListOCIV1
|
||||
var err error
|
||||
var imageStr string
|
||||
ctx := getContext()
|
||||
|
||||
store := *d.store
|
||||
systemContext := &image_types.SystemContext{}
|
||||
|
||||
iopts := d.inspectOpts
|
||||
for _, image := range images {
|
||||
switch iopts.InspectType {
|
||||
case inspectTypeContainer:
|
||||
builder, err = openBuilder(ctx, store, image)
|
||||
if err != nil {
|
||||
builder, err = openImage(ctx, systemContext, store, image)
|
||||
var manifestErr error
|
||||
if err != nil {
|
||||
if imageStr, manifestErr = manifestInspect(ctx, store, systemContext, image); manifestErr != nil {
|
||||
logger.Error(manifestErr)
|
||||
continue
|
||||
}
|
||||
ociImage, err := inspectImage(imageStr)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
imageList = append(imageList, *ociImage)
|
||||
continue
|
||||
}
|
||||
}
|
||||
case inspectTypeImage:
|
||||
builder, err = openImage(ctx, systemContext, store, image)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
case inspectTypeManifest:
|
||||
imageStr, err = manifestInspect(ctx, store, systemContext, image)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ociImage, err := inspectImage(imageStr)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
imageList = append(imageList, *ociImage)
|
||||
continue
|
||||
|
||||
default:
|
||||
logger.Error("the only recognized types are %q and %q", inspectTypeContainer, inspectTypeImage)
|
||||
continue
|
||||
}
|
||||
out := buildah.GetBuildInfo(builder)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
imageList = append(imageList, out.OCIv1)
|
||||
}
|
||||
|
||||
return imageList, nil
|
||||
}
|
||||
|
||||
func inspectImage(data string) (*v1.Image, error) {
|
||||
if data != "" {
|
||||
var outStruct map[string]interface{}
|
||||
err := json.Unmarshal([]byte(data), &outStruct)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "decode out json from image inspect failed")
|
||||
}
|
||||
imageData, _, err := unstructured.NestedFieldCopy(outStruct, "OCIv1")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "decode out json from OCIv1 object failed")
|
||||
}
|
||||
img := &v1.Image{}
|
||||
err = json2.Convert(imageData, img)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "decode OCIv1 to v1.Image failed")
|
||||
}
|
||||
return img, nil
|
||||
}
|
||||
return nil, errors.New("inspect output is empty")
|
||||
}
|
||||
|
||||
func newInspectOpts() *types.InspectResults {
|
||||
return &types.InspectResults{
|
||||
Format: "",
|
||||
InspectType: inspectTypeContainer,
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
// Copyright © 2022 sealos.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package image
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
|
||||
"github.com/containers/buildah"
|
||||
"github.com/containers/buildah/define"
|
||||
buildahcli "github.com/containers/buildah/pkg/cli"
|
||||
"github.com/containers/buildah/pkg/parse"
|
||||
"github.com/containers/common/pkg/auth"
|
||||
image_types "github.com/containers/image/v5/types"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
labring_types "github.com/labring/sealos/pkg/image/types"
|
||||
)
|
||||
|
||||
func (d *Service) Load(archiveName string) (string, error) {
|
||||
if err := buildahcli.VerifyFlagsArgsOrder([]string{archiveName}); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := auth.CheckAuthFile(d.pullOpts.Authfile); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
store := *d.store
|
||||
systemContext := &image_types.SystemContext{}
|
||||
|
||||
platforms := []struct{ OS, Arch, Variant string }{{d.pullOpts.OS, d.pullOpts.Arch, d.pullOpts.Variant}}
|
||||
|
||||
if len(platforms) > 1 {
|
||||
logger.Warn("ignoring platforms other than %+v: %+v", platforms[0], platforms[1:])
|
||||
}
|
||||
decConfig, err := getDecryptConfig(d.pullOpts.DecryptionKeys)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "unable to obtain decrypt config")
|
||||
}
|
||||
|
||||
policy, ok := define.PolicyMap[d.pullOpts.PullPolicy]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("unsupported pull policy %q", d.pullOpts.PullPolicy)
|
||||
}
|
||||
options := buildah.PullOptions{
|
||||
SignaturePolicyPath: d.pullOpts.SignaturePolicy,
|
||||
Store: store,
|
||||
SystemContext: systemContext,
|
||||
BlobDirectory: d.pullOpts.BlobCache,
|
||||
AllTags: d.pullOpts.AllTags,
|
||||
ReportWriter: os.Stderr,
|
||||
RemoveSignatures: d.pullOpts.RemoveSignatures,
|
||||
MaxRetries: maxPullPushRetries,
|
||||
RetryDelay: pullPushRetryDelay,
|
||||
OciDecryptConfig: decConfig,
|
||||
PullPolicy: policy,
|
||||
}
|
||||
if d.pullOpts.Quiet {
|
||||
options.ReportWriter = nil // Turns off logging output
|
||||
}
|
||||
id, err := buildah.Pull(getContext(), fmt.Sprintf("%s:%s", labring_types.DefaultTransport, archiveName), options)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
logger.Info("load image %s", id)
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func newPullOptions() *labring_types.PullOptions {
|
||||
return &labring_types.PullOptions{
|
||||
AllTags: false,
|
||||
Authfile: auth.GetDefaultAuthFile(),
|
||||
BlobCache: "",
|
||||
CertDir: "",
|
||||
Creds: "",
|
||||
PullPolicy: "missing",
|
||||
SignaturePolicy: "",
|
||||
RemoveSignatures: false,
|
||||
DecryptionKeys: nil,
|
||||
TLSVerify: true,
|
||||
Arch: runtime.GOARCH,
|
||||
OS: runtime.GOOS,
|
||||
Platform: []string{parse.DefaultPlatform()},
|
||||
Variant: "",
|
||||
}
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
// Copyright © 2022 buildah.
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://github.com/containers/buildah/blob/main/LICENSE
|
||||
//
|
||||
// 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 image
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
|
||||
"github.com/containers/buildah/util"
|
||||
"github.com/containers/common/libimage"
|
||||
"github.com/containers/common/libimage/manifests"
|
||||
cp "github.com/containers/image/v5/copy"
|
||||
"github.com/containers/image/v5/manifest"
|
||||
"github.com/containers/image/v5/transports"
|
||||
"github.com/containers/image/v5/transports/alltransports"
|
||||
"github.com/containers/image/v5/types"
|
||||
"github.com/containers/storage"
|
||||
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
labring_types "github.com/labring/sealos/pkg/image/types"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
)
|
||||
|
||||
func manifestInspect(ctx context.Context, store storage.Store, systemContext *types.SystemContext, imageSpec string) (string, error) {
|
||||
runtime, err := libimage.RuntimeFromStore(store, &libimage.RuntimeOptions{SystemContext: systemContext})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
printManifest := func(manifest []byte) (string, error) {
|
||||
var b bytes.Buffer
|
||||
err = json.Indent(&b, manifest, "", " ")
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error rendering manifest for display")
|
||||
}
|
||||
//fmt.Printf("%s\n", b.String())
|
||||
return b.String(), nil
|
||||
}
|
||||
|
||||
// Before doing a remote lookup, attempt to resolve the manifest list
|
||||
// locally.
|
||||
manifestList, err := runtime.LookupManifestList(imageSpec)
|
||||
switch errors.Cause(err) {
|
||||
case storage.ErrImageUnknown, libimage.ErrNotAManifestList:
|
||||
// We need to do the remote inspection below.
|
||||
case nil:
|
||||
schema2List, err := manifestList.Inspect()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
rawSchema2List, err := json.Marshal(schema2List)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return printManifest(rawSchema2List)
|
||||
|
||||
default:
|
||||
// Fatal error.
|
||||
return "", err
|
||||
}
|
||||
|
||||
// TODO: at some point `libimage` should support resolving manifests
|
||||
// like that. Similar to `libimage.Runtime.LookupImage` we could
|
||||
// implement a `*.LookupImageIndex`.
|
||||
refs, err := util.ResolveNameToReferences(store, systemContext, imageSpec)
|
||||
if err != nil {
|
||||
logger.Debug("error parsing reference to image %q: %v", imageSpec, err)
|
||||
}
|
||||
|
||||
if ref, _, err := util.FindImage(store, "", systemContext, imageSpec); err == nil {
|
||||
refs = append(refs, ref)
|
||||
} else if ref, err := alltransports.ParseImageName(imageSpec); err == nil {
|
||||
refs = append(refs, ref)
|
||||
}
|
||||
if len(refs) == 0 {
|
||||
return "", errors.Errorf("error locating images with names %v", imageSpec)
|
||||
}
|
||||
|
||||
var (
|
||||
latestErr error
|
||||
result []byte
|
||||
)
|
||||
|
||||
appendErr := func(e error) {
|
||||
if latestErr == nil {
|
||||
latestErr = e
|
||||
} else {
|
||||
latestErr = errors.Wrapf(latestErr, "tried %v", e)
|
||||
}
|
||||
}
|
||||
|
||||
for _, ref := range refs {
|
||||
logger.Debug("Testing reference %q for possible manifest", transports.ImageName(ref))
|
||||
|
||||
src, err := ref.NewImageSource(ctx, systemContext)
|
||||
if err != nil {
|
||||
appendErr(errors.Wrapf(err, "reading image %q", transports.ImageName(ref)))
|
||||
continue
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
manifestBytes, manifestType, err := src.GetManifest(ctx, nil)
|
||||
if err != nil {
|
||||
appendErr(errors.Wrapf(err, "loading manifest %q", transports.ImageName(ref)))
|
||||
continue
|
||||
}
|
||||
|
||||
if !manifest.MIMETypeIsMultiImage(manifestType) {
|
||||
appendErr(errors.Errorf("manifest is of type %s (not a list type)", manifestType))
|
||||
continue
|
||||
}
|
||||
result = manifestBytes
|
||||
break
|
||||
}
|
||||
if len(result) == 0 && latestErr != nil {
|
||||
return "", latestErr
|
||||
}
|
||||
|
||||
return printManifest(result)
|
||||
}
|
||||
|
||||
func manifestPush(systemContext *types.SystemContext, store storage.Store, listImageSpec, destSpec string, opts *labring_types.PushOptions) error {
|
||||
runtime, err := libimage.RuntimeFromStore(store, &libimage.RuntimeOptions{SystemContext: systemContext})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
manifestList, err := runtime.LookupManifestList(listImageSpec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, list, err := manifests.LoadFromImage(store, manifestList.ID())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dest, err := alltransports.ParseImageName(destSpec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var manifestType string
|
||||
if opts.Format != "" {
|
||||
switch opts.Format {
|
||||
case "oci":
|
||||
manifestType = imgspecv1.MediaTypeImageManifest
|
||||
case "v2s2", "docker":
|
||||
manifestType = manifest.DockerV2Schema2MediaType
|
||||
default:
|
||||
return errors.Errorf("unknown format %q. Choose on of the supported formats: 'oci' or 'v2s2'", opts.Format)
|
||||
}
|
||||
}
|
||||
|
||||
options := manifests.PushOptions{
|
||||
Store: store,
|
||||
SystemContext: systemContext,
|
||||
ImageListSelection: cp.CopySpecificImages,
|
||||
Instances: nil,
|
||||
RemoveSignatures: opts.RemoveSignatures,
|
||||
SignBy: opts.SignBy,
|
||||
ManifestType: manifestType,
|
||||
}
|
||||
if opts.All {
|
||||
options.ImageListSelection = cp.CopyAllImages
|
||||
}
|
||||
if !opts.Quiet {
|
||||
options.ReportWriter = os.Stderr
|
||||
}
|
||||
|
||||
_, digest, err := list.Push(getContext(), dest, options)
|
||||
|
||||
if err == nil && opts.Rm {
|
||||
_, err = store.DeleteImage(manifestList.ID(), true)
|
||||
}
|
||||
|
||||
if opts.Digestfile != "" {
|
||||
if err = os.WriteFile(opts.Digestfile, []byte(digest.String()), 0644); err != nil {
|
||||
return util.GetFailureCause(err, errors.Wrapf(err, "failed to write digest to file %q", opts.Digestfile))
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
// Copyright © 2022 sealos.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package image
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/containers/common/libimage"
|
||||
image_types "github.com/containers/image/v5/types"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
)
|
||||
|
||||
func (d *Service) Rmi(prune, force bool, names []string) error {
|
||||
store := *d.store
|
||||
systemContext := &image_types.SystemContext{}
|
||||
runtime, err := libimage.RuntimeFromStore(store, &libimage.RuntimeOptions{SystemContext: systemContext})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options := &libimage.RemoveImagesOptions{
|
||||
Filters: []string{"readonly=false"},
|
||||
}
|
||||
iopts := d.rmiOpts
|
||||
if prune {
|
||||
options.Filters = append(options.Filters, "dangling=true")
|
||||
} else if !iopts.All {
|
||||
options.Filters = append(options.Filters, "intermediate=false")
|
||||
}
|
||||
options.Force = force
|
||||
|
||||
rmiReports, rmiErrors := runtime.RemoveImages(context.Background(), names, options)
|
||||
for _, r := range rmiReports {
|
||||
for _, u := range r.Untagged {
|
||||
fmt.Printf("untagged: %s\n", u)
|
||||
}
|
||||
}
|
||||
for _, r := range rmiReports {
|
||||
if r.Removed {
|
||||
fmt.Printf("%s\n", r.ID)
|
||||
}
|
||||
}
|
||||
|
||||
var multiE *multierror.Error
|
||||
multiE = multierror.Append(multiE, rmiErrors...)
|
||||
return multiE.ErrorOrNil()
|
||||
}
|
||||
|
||||
func (d *Service) Prune() error {
|
||||
return d.Rmi(true, d.rmiOpts.Force, []string{})
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
// Copyright © 2022 sealos.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package image
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/containers/common/pkg/config"
|
||||
"github.com/containers/common/pkg/umask"
|
||||
is "github.com/containers/image/v5/storage"
|
||||
"github.com/containers/storage"
|
||||
"github.com/containers/storage/pkg/unshare"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/labring/sealos/pkg/image/types"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
)
|
||||
|
||||
// Service is the default service, which is used for image pull/push
|
||||
type Service struct {
|
||||
store *storage.Store
|
||||
globalFlagResults *types.GlobalBuildahFlags
|
||||
listImageOpts *types.ImageResults
|
||||
inspectOpts *types.InspectResults
|
||||
pullOpts *types.PullOptions
|
||||
pushOpts *types.PushOptions
|
||||
rmiOpts *types.RmiOptions
|
||||
buildahBuildOptions *types.BuildahBuildOptions
|
||||
}
|
||||
|
||||
func newGlobalOptions() *types.GlobalBuildahFlags {
|
||||
var (
|
||||
defaultStoreDriverOptions []string
|
||||
)
|
||||
storageOptions, err := storage.DefaultStoreOptions(false, 0)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
if len(storageOptions.GraphDriverOptions) > 0 {
|
||||
optionSlice := storageOptions.GraphDriverOptions[:]
|
||||
defaultStoreDriverOptions = optionSlice
|
||||
}
|
||||
containerConfig, err := config.Default()
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
containerConfig.CheckCgroupsAndAdjustConfig()
|
||||
return &types.GlobalBuildahFlags{
|
||||
Debug: true,
|
||||
LogLevel: "warn",
|
||||
Root: storageOptions.GraphRoot,
|
||||
RunRoot: storageOptions.RunRoot,
|
||||
StorageDriver: storageOptions.GraphDriverName,
|
||||
RegistriesConf: "",
|
||||
RegistriesConfDir: "",
|
||||
DefaultMountsFile: "",
|
||||
StorageOpts: defaultStoreDriverOptions,
|
||||
UserNSUID: []string{},
|
||||
UserNSGID: []string{},
|
||||
CPUProfile: "",
|
||||
MemoryProfile: "",
|
||||
UserShortNameAliasConfPath: "",
|
||||
CgroupManager: containerConfig.Engine.CgroupManager,
|
||||
}
|
||||
}
|
||||
|
||||
func newListImageOptions() *types.ImageResults {
|
||||
return &types.ImageResults{
|
||||
ImageOptions: types.ImageOptions{
|
||||
All: false,
|
||||
Digests: false,
|
||||
Format: "",
|
||||
JSON: false,
|
||||
NoHeading: false,
|
||||
Truncate: false,
|
||||
Quiet: false,
|
||||
History: false,
|
||||
},
|
||||
Filter: []string{},
|
||||
Names: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
func newStore(globalFlagResults *types.GlobalBuildahFlags) (*storage.Store, error) {
|
||||
options, err := storage.DefaultStoreOptions(unshare.IsRootless(), unshare.GetRootlessUID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
options.GraphRoot = globalFlagResults.Root
|
||||
options.RunRoot = globalFlagResults.RunRoot
|
||||
|
||||
if err := setXDGRuntimeDir(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
options.GraphDriverName = globalFlagResults.StorageDriver
|
||||
// If any options setup in config, these should be dropped if user overrode the driver
|
||||
options.GraphDriverOptions = []string{}
|
||||
options.GraphDriverOptions = globalFlagResults.StorageOpts
|
||||
|
||||
// Do not allow to mount a graphdriver that is not vfs if we are creating the userns as part
|
||||
// of the mount command.
|
||||
// Differently, allow the mount if we are already in a userns, as the mount point will still
|
||||
// be accessible once "buildah mount" exits.
|
||||
if os.Geteuid() != 0 && options.GraphDriverName != "vfs" {
|
||||
return nil, errors.Errorf("cannot mount using driver %s in rootless mode. You need to run it in a `buildah unshare` session", options.GraphDriverName)
|
||||
}
|
||||
|
||||
if len(globalFlagResults.UserNSUID) > 0 {
|
||||
uopts := globalFlagResults.UserNSUID
|
||||
gopts := globalFlagResults.UserNSGID
|
||||
|
||||
if len(gopts) == 0 {
|
||||
gopts = uopts
|
||||
}
|
||||
|
||||
uidmap, gidmap, err := unshare.ParseIDMappings(uopts, gopts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options.UIDMap = uidmap
|
||||
options.GIDMap = gidmap
|
||||
} else {
|
||||
if len(globalFlagResults.UserNSGID) > 0 {
|
||||
return nil, errors.New("option --userns-gid-map can not be used without --userns-uid-map")
|
||||
}
|
||||
}
|
||||
|
||||
// If a subcommand has the flags, check if they are set; if so, override the global values
|
||||
uopts := globalFlagResults.UserNSUID
|
||||
gopts := globalFlagResults.UserNSGID
|
||||
if len(gopts) == 0 {
|
||||
gopts = uopts
|
||||
}
|
||||
uidmap, gidmap, err := unshare.ParseIDMappings(uopts, gopts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options.UIDMap = uidmap
|
||||
options.GIDMap = gidmap
|
||||
umask.Check()
|
||||
store, err := storage.GetStore(options)
|
||||
if store != nil {
|
||||
is.Transport.SetStore(store)
|
||||
}
|
||||
return &store, err
|
||||
}
|
||||
|
||||
func newRmiOptions() *types.RmiOptions {
|
||||
return &types.RmiOptions{
|
||||
Prune: false,
|
||||
All: false,
|
||||
Force: false,
|
||||
}
|
||||
}
|
||||
|
||||
func NewImageService() (types.ImageService, error) {
|
||||
globalFlagResults := newGlobalOptions()
|
||||
store, err := newStore(globalFlagResults)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Service{
|
||||
store: store,
|
||||
globalFlagResults: globalFlagResults,
|
||||
listImageOpts: newListImageOptions(),
|
||||
inspectOpts: newInspectOpts(),
|
||||
pullOpts: newPullOptions(),
|
||||
pushOpts: newPushOptions(),
|
||||
rmiOpts: newRmiOptions(),
|
||||
buildahBuildOptions: newBuildahBuildOptions(),
|
||||
}, nil
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
// Copyright © 2022 sealos.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package image
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/buildah"
|
||||
"github.com/containers/buildah/define"
|
||||
"github.com/containers/buildah/util"
|
||||
"github.com/containers/common/pkg/auth"
|
||||
"github.com/containers/image/v5/manifest"
|
||||
"github.com/containers/image/v5/pkg/compression"
|
||||
"github.com/containers/image/v5/transports"
|
||||
"github.com/containers/image/v5/transports/alltransports"
|
||||
image_types "github.com/containers/image/v5/types"
|
||||
"github.com/containers/storage"
|
||||
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
labring_types "github.com/labring/sealos/pkg/image/types"
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
)
|
||||
|
||||
// using buildah push
|
||||
|
||||
func (d *Service) Save(imageName, archiveName string) error {
|
||||
if err := auth.CheckAuthFile(d.pushOpts.Authfile); err != nil {
|
||||
return err
|
||||
}
|
||||
compress := define.Gzip
|
||||
if d.pushOpts.DisableCompression {
|
||||
compress = define.Uncompressed
|
||||
}
|
||||
var manifestType string
|
||||
if d.pushOpts.Format != "" {
|
||||
switch d.pushOpts.Format {
|
||||
case "oci":
|
||||
manifestType = imgspecv1.MediaTypeImageManifest
|
||||
case "v2s1":
|
||||
manifestType = manifest.DockerV2Schema1SignedMediaType
|
||||
case "v2s2", "docker":
|
||||
manifestType = manifest.DockerV2Schema2MediaType
|
||||
default:
|
||||
return errors.Errorf("unknown format %q. Choose on of the supported formats: 'oci', 'v2s1', or 'v2s2'", d.pushOpts.Format)
|
||||
}
|
||||
}
|
||||
store := *d.store
|
||||
systemContext := &image_types.SystemContext{}
|
||||
encConfig, encLayers, err := getEncryptConfig(d.pushOpts.EncryptionKeys, d.pushOpts.EncryptLayers)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to obtain encryption config")
|
||||
}
|
||||
options := buildah.PushOptions{
|
||||
Compression: compress,
|
||||
ManifestType: manifestType,
|
||||
SignaturePolicyPath: d.pushOpts.SignaturePolicy,
|
||||
Store: store,
|
||||
SystemContext: systemContext,
|
||||
BlobDirectory: d.pushOpts.BlobCache,
|
||||
RemoveSignatures: d.pushOpts.RemoveSignatures,
|
||||
SignBy: d.pushOpts.SignBy,
|
||||
MaxRetries: maxPullPushRetries,
|
||||
RetryDelay: pullPushRetryDelay,
|
||||
OciEncryptConfig: encConfig,
|
||||
OciEncryptLayers: encLayers,
|
||||
}
|
||||
if !d.pushOpts.Quiet {
|
||||
options.ReportWriter = os.Stderr
|
||||
}
|
||||
if d.pushOpts.CompressionFormat != "" {
|
||||
algo, err := compression.AlgorithmByName(d.pushOpts.CompressionFormat)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options.CompressionFormat = &algo
|
||||
}
|
||||
options.CompressionLevel = &d.pushOpts.CompressionLevel
|
||||
|
||||
// Default here
|
||||
destSpec := fmt.Sprintf("%s:%s:%s", labring_types.DefaultTransport, archiveName, imageName)
|
||||
dest, err := alltransports.ParseImageName(destSpec)
|
||||
// add the docker:// transport to see if they neglected it.
|
||||
if err != nil {
|
||||
destTransport := strings.Split(destSpec, ":")[0]
|
||||
if t := transports.Get(destTransport); t != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if strings.Contains(destSpec, "://") {
|
||||
return err
|
||||
}
|
||||
|
||||
destSpec = "docker://" + destSpec
|
||||
dest2, err2 := alltransports.ParseImageName(destSpec)
|
||||
if err2 != nil {
|
||||
return err
|
||||
}
|
||||
dest = dest2
|
||||
logger.Debug("Assuming docker:// as the transport method for DESTINATION: %s", destSpec)
|
||||
}
|
||||
|
||||
ref, digest, err := buildah.Push(getContext(), imageName, dest, options)
|
||||
if err != nil {
|
||||
if errors.Cause(err) != storage.ErrImageUnknown {
|
||||
// Image might be a manifest so attempt a manifest push
|
||||
if manifestsErr := manifestPush(systemContext, store, imageName, destSpec, d.pushOpts); manifestsErr == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return util.GetFailureCause(err, errors.Wrapf(err, "error pushing image %q to %q", imageName, archiveName))
|
||||
}
|
||||
if ref != nil {
|
||||
logger.Debug("pushed image %q with digest %s", ref, digest.String())
|
||||
} else {
|
||||
logger.Debug("pushed image with digest %s", digest.String())
|
||||
}
|
||||
if d.pushOpts.Digestfile != "" {
|
||||
if err = os.WriteFile(d.pushOpts.Digestfile, []byte(digest.String()), 0644); err != nil {
|
||||
return util.GetFailureCause(err, errors.Wrapf(err, "failed to write digest to file %q", d.pushOpts.Digestfile))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newPushOptions() *labring_types.PushOptions {
|
||||
return &labring_types.PushOptions{
|
||||
All: false,
|
||||
Authfile: auth.GetDefaultAuthFile(),
|
||||
BlobCache: "",
|
||||
CertDir: "",
|
||||
Creds: "",
|
||||
Digestfile: "",
|
||||
DisableCompression: false,
|
||||
Format: "oci",
|
||||
CompressionFormat: "",
|
||||
CompressionLevel: 0,
|
||||
Quiet: false,
|
||||
Rm: false,
|
||||
SignBy: "",
|
||||
SignaturePolicy: "",
|
||||
RemoveSignatures: false,
|
||||
EncryptionKeys: nil,
|
||||
TLSVerify: true,
|
||||
EncryptLayers: nil,
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
// Copyright © 2022 sealos.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package image
|
||||
|
||||
import (
|
||||
"github.com/containers/common/libimage"
|
||||
image_types "github.com/containers/image/v5/types"
|
||||
)
|
||||
|
||||
func (d *Service) Tag(src, dst string) error {
|
||||
store := *d.store
|
||||
systemContext := &image_types.SystemContext{}
|
||||
|
||||
runtime, err := libimage.RuntimeFromStore(store, &libimage.RuntimeOptions{SystemContext: systemContext})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Allow tagging manifest list instead of resolving instances from manifest
|
||||
lookupOptions := &libimage.LookupImageOptions{ManifestList: true}
|
||||
image, _, err := runtime.LookupImage(src, lookupOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return image.Tag(dst)
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
// Copyright © 2022 buidah.
|
||||
//
|
||||
// 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 registry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/containers/common/pkg/auth"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (*Service) Login(domain, username, passwd string) error {
|
||||
var (
|
||||
opts = loginReply{
|
||||
loginOpts: auth.LoginOptions{
|
||||
Stdin: os.Stdin,
|
||||
Stdout: os.Stdout,
|
||||
AcceptRepositories: true,
|
||||
AuthFile: auth.GetDefaultAuthFile(),
|
||||
CertDir: "",
|
||||
Password: passwd,
|
||||
Username: username,
|
||||
StdinPassword: false,
|
||||
GetLoginSet: false,
|
||||
Verbose: false,
|
||||
},
|
||||
tlsVerify: false,
|
||||
getLogin: true,
|
||||
}
|
||||
)
|
||||
if err := setXDGRuntimeDir(); err != nil {
|
||||
return err
|
||||
}
|
||||
systemContext, err := getSystemContext(opts.tlsVerify)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error building system context")
|
||||
}
|
||||
|
||||
return auth.Login(context.TODO(), systemContext, &opts.loginOpts, []string{domain})
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
// Copyright © 2022 buildah.
|
||||
//
|
||||
// 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 registry
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/containers/common/pkg/auth"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (*Service) Logout(domain string) error {
|
||||
var (
|
||||
opts = auth.LogoutOptions{
|
||||
Stdout: os.Stdout,
|
||||
AcceptRepositories: true,
|
||||
AuthFile: auth.GetDefaultAuthFile(),
|
||||
All: false,
|
||||
}
|
||||
tlsVerify = false
|
||||
)
|
||||
|
||||
if err := setXDGRuntimeDir(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
systemContext, err := getSystemContext(tlsVerify)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error building system context")
|
||||
}
|
||||
return auth.Logout(systemContext, &opts, []string{domain})
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
// Copyright © 2022 sealos.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package registry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
|
||||
"github.com/labring/sealos/pkg/utils/logger"
|
||||
|
||||
"github.com/labring/sealos/pkg/image/types"
|
||||
|
||||
"github.com/containers/buildah"
|
||||
"github.com/containers/buildah/define"
|
||||
"github.com/containers/buildah/pkg/parse"
|
||||
"github.com/containers/common/pkg/auth"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
}
|
||||
|
||||
type pullOptions struct {
|
||||
allTags bool
|
||||
authfile string
|
||||
blobCache string
|
||||
certDir string
|
||||
creds string
|
||||
signaturePolicy string
|
||||
quiet bool
|
||||
removeSignatures bool
|
||||
tlsVerify bool
|
||||
decryptionKeys []string
|
||||
pullPolicy string
|
||||
}
|
||||
|
||||
func (*Service) Pull(platform v1.Platform, policy string, images ...string) error {
|
||||
opt := pullOptions{
|
||||
allTags: false,
|
||||
authfile: auth.GetDefaultAuthFile(),
|
||||
blobCache: "",
|
||||
certDir: "",
|
||||
creds: "",
|
||||
signaturePolicy: "",
|
||||
quiet: false,
|
||||
removeSignatures: false,
|
||||
tlsVerify: false,
|
||||
decryptionKeys: nil,
|
||||
pullPolicy: policy, //missing, always, never, ifnewer
|
||||
}
|
||||
|
||||
if err := auth.CheckAuthFile(opt.authfile); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pullCmdFlag := getCmdFlag()
|
||||
_ = pullCmdFlag.Flag("tls-verify").Value.Set("false")
|
||||
pullCmdFlag.Flag("tls-verify").Changed = true
|
||||
|
||||
systemContext, _ := parse.SystemContextFromOptions(pullCmdFlag)
|
||||
|
||||
decConfig, err := getDecryptConfig(opt.decryptionKeys)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to obtain decrypt config")
|
||||
}
|
||||
|
||||
pullPolicy, ok := define.PolicyMap[opt.pullPolicy]
|
||||
if !ok {
|
||||
return fmt.Errorf("unsupported pull policy %q", "missing")
|
||||
}
|
||||
|
||||
globalFlagResults := newGlobalOptions()
|
||||
store, err := getStore(globalFlagResults)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
systemContext.OSChoice = platform.OS
|
||||
systemContext.ArchitectureChoice = platform.Architecture
|
||||
systemContext.VariantChoice = platform.Variant
|
||||
logger.Info("pulling images %v for platform %s", images, fmt.Sprintf("%s/%s", systemContext.OSChoice, systemContext.ArchitectureChoice))
|
||||
opts := buildah.PullOptions{
|
||||
SignaturePolicyPath: opt.signaturePolicy,
|
||||
Store: store,
|
||||
SystemContext: systemContext,
|
||||
BlobDirectory: opt.blobCache,
|
||||
AllTags: opt.allTags,
|
||||
ReportWriter: os.Stderr,
|
||||
RemoveSignatures: opt.removeSignatures,
|
||||
MaxRetries: 3,
|
||||
RetryDelay: 2 * time.Second,
|
||||
OciDecryptConfig: decConfig,
|
||||
PullPolicy: pullPolicy,
|
||||
}
|
||||
|
||||
for _, image := range images {
|
||||
imageID, err := buildah.Pull(context.TODO(), image, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(imageID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewRegistryService() (types.RegistryService, error) {
|
||||
return &Service{}, nil
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user