Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
The diff you're trying to view is too large. We only load the first 3000 changed files.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/.GOPATH
/bin
/container-feeder
110 changes: 55 additions & 55 deletions feeder/crio_feeder.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,37 +18,66 @@
package feeder

import (
"context"
"fmt"
"io"
"io/ioutil"
"os"

"github.com/containers/image/docker/reference"
"github.com/containers/storage"
"github.com/containers/common/libimage"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/podman/v4/libpod"
"github.com/containers/storage/pkg/reexec"
"github.com/projectatomic/libpod/libpod"
"github.com/containers/storage/pkg/unshare"

log "github.com/sirupsen/logrus"
"github.com/syndtr/gocapability/capability"
)

// CRIOFeeder wraps the libpod.Runtime and implementes the Feeder interface.
type CRIOFeeder struct {
runtime *libpod.Runtime
runtime *libpod.Runtime
backgroundContext context.Context
}

// capabilities for running in a user namespace
var neededCapabilities = []capability.Cap{
capability.CAP_CHOWN,
capability.CAP_DAC_OVERRIDE,
capability.CAP_FOWNER,
capability.CAP_FSETID,
capability.CAP_MKNOD,
capability.CAP_SETFCAP,
}

func reexecForRootlessStorage() error {
capabilities, err := capability.NewPid2(0)
if err != nil {
return err
}
err = capabilities.Load()
if err != nil {
return err
}
for _, cap := range neededCapabilities {
if !capabilities.Get(capability.EFFECTIVE, cap) {
// We miss a capability we need, create a user namespaces
unshare.MaybeReexecUsingUserNamespace(true)
}
}
return nil
}

// NewCRIOFeeder returns a pointer to an initialized CRIOFeeder.
func NewCRIOFeeder() (*CRIOFeeder, error) {
feeder := &CRIOFeeder{}
feeder := &CRIOFeeder{backgroundContext: context.Background()}

if reexec.Init() {
return nil, fmt.Errorf("could not init CRIOFeeder")
}

options := []libpod.RuntimeOption{}
storageOpts := storage.DefaultStoreOptions
options = append(options, libpod.WithStorageConfig(storageOpts))
if err := reexecForRootlessStorage(); err != nil {
return nil, err
}

runtime, err := libpod.NewRuntime(options...)
runtime, err := libpod.NewRuntime(feeder.backgroundContext)
if err != nil {
return nil, fmt.Errorf("error getting libpod runtime: %v", err)
}
Expand All @@ -61,67 +90,38 @@ func NewCRIOFeeder() (*CRIOFeeder, error) {
func (f *CRIOFeeder) Images() ([]string, error) {
tags := []string{}

images, err := f.runtime.GetImageResults()
imgs := []string{}
images, err := f.runtime.LibimageRuntime().ListImages(f.backgroundContext, imgs, nil)
if err != nil {
return nil, fmt.Errorf("error getting images from libpod: %v", err)
}

for _, img := range images {
tags = append(tags, img.RepoTags...)
imgTags, err := img.RepoTags()
if err != nil {
return nil, err
}
tags = append(tags, imgTags...)
}

return tags, nil
}

// decompressXZImage decompresses the specified tar.xz into a tar image that is
// located in /var/tmp (writable on MicroOS).
func decompressXZImage(image string) (string, error) {
log.Debugf("Decompressing image %s", image)

tmpFile, err := ioutil.TempFile("/var/tmp", "container-feeder")
if err != nil {
return "", fmt.Errorf("error creating temporary file: %v", err)
}
defer tmpFile.Close()

cmd := []string{"/usr/bin/unxz", "-c", image}
if err := runCommand(cmd, "", tmpFile); err != nil {
return "", fmt.Errorf("error using xz: %v", err)
}

return tmpFile.Name(), nil
}

// LoadImage loads the specified image into containers/storage and returns the
// image name.
func (f *CRIOFeeder) LoadImage(path string) (string, error) {
var writer io.Writer
options := libpod.CopyOptions{
Writer: writer,
}

image, err := decompressXZImage(path)
imgs, err := f.runtime.LibimageRuntime().Load(f.backgroundContext, path, nil)
if err != nil {
return "", err
}
defer os.Remove(image)

src := libpod.DockerArchive + ":" + image
imgName, err := f.runtime.PullImage(src, options)
if err != nil {
return "", fmt.Errorf("error loading image: %v", err)
}

log.Debugf("Loaded image: %v", imgName)
return imgName, nil
log.Debugf("Loaded image(s): %v", imgs)
return imgs[0], nil
}

// TagImage tags the specified image with the supplied tags.
func (f *CRIOFeeder) TagImage(image string, tags []string) error {
newImage := f.runtime.NewImage(image)
newImage.GetLocalImageName()

img, err := f.runtime.GetImage(newImage.LocalName)
img, _, err := f.runtime.LibimageRuntime().LookupImage(image, nil)
if err != nil {
return err
}
Expand All @@ -137,15 +137,15 @@ func (f *CRIOFeeder) TagImage(image string, tags []string) error {
}

// addImageNames adds addNames to the specified image
func (f *CRIOFeeder) addImageNames(image *storage.Image, names []string) error {
func (f *CRIOFeeder) addImageNames(image *libimage.Image, names []string) error {
// Add tags to the names if applicable
tags, err := expandedTags(names)
if err != nil {
return err
}
for _, tag := range tags {
if err := f.runtime.TagImage(image, tag); err != nil {
return fmt.Errorf("error adding name (%v) to image %q", tag, image.ID)
if err := image.Tag(tag); err != nil {
return fmt.Errorf("error adding name (%v) to image %q", tag, image.ID())
}
}
return nil
Expand Down
59 changes: 38 additions & 21 deletions feeder/docker_feeder.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ package feeder

import (
"context"
"io/ioutil"
"encoding/json"
"errors"
"io"
"os"
"os/exec"
"strings"

"github.com/docker/docker/api/types"
Expand All @@ -48,37 +49,43 @@ func NewDockerFeeder() (*DockerFeeder, error) {
return feeder, nil
}

// dockerDaemonAPIVersion returns the API version supported by the server by
// shelling out.
func dockerDaemonAPIVersion() (string, error) {
out, err := exec.Command(
"docker",
"version",
"--format",
"{{.Server.APIVersion}}").Output()
if err != nil {
return "", err
}
api := strings.Trim(string(out[:]), "\n")
return api, nil
}

// connectToDaemon returns a Docker client.Client using the right version of
// the API
func connectToDaemon() (*client.Client, error) {
// Set the exact version of the API in use, otherwise the library will
// try to use the latest one, which might be too new compared to the
// one supported by the docker daemon

apiVersion, err := dockerDaemonAPIVersion()
cli, err := client.NewEnvClient()
if err != nil {
return nil, err
}
vers, err := cli.ServerVersion(context.Background())
if err != nil {
return nil, err
}

var apiVersion string = ""

for _, component := range vers.Components {
if component.Name != "Engine" {
continue
}
if vers, found := component.Details["ApiVersion"]; found {
apiVersion = vers
}
}

if apiVersion == "" {
return nil, errors.New("Could not obtain docker Engine API version")
}

if err := os.Setenv("DOCKER_API_VERSION", apiVersion); err != nil {
return nil, err
}

cli, err := client.NewEnvClient()
cli, err = client.NewEnvClient()

if err != nil {
return nil, err
}
Expand Down Expand Up @@ -108,6 +115,10 @@ func (f *DockerFeeder) Images() ([]string, error) {
return tags, nil
}

type loadResponse struct {
Stream string `json:stream`
}

// LoadImage loads the specified image into docker. Returns the image name
// loaded into the docker daemon.
func (f *DockerFeeder) LoadImage(pathToImage string) (string, error) {
Expand All @@ -122,12 +133,18 @@ func (f *DockerFeeder) LoadImage(pathToImage string) (string, error) {
return "", err
}
defer ret.Body.Close()
b, err := ioutil.ReadAll(ret.Body)
b, err := io.ReadAll(ret.Body)
if err != nil {
return "", err
}

loadResp := loadResponse{}
err = json.Unmarshal(b, &loadResp)
if err != nil {
return "", err
}

return strings.TrimSpace(strings.TrimPrefix(string(b[:]), "Loaded image:")), nil
return strings.TrimSpace(strings.TrimPrefix(string(loadResp.Stream), "Loaded image:")), nil
}

// TagImage tags the specified docker image with the supplied tags.
Expand Down
40 changes: 9 additions & 31 deletions feeder/feeder.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,15 @@
package feeder

import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"regexp"

"github.com/containers/image/docker/reference"

wlk "github.com/kubic-project/container-feeder/walker"
wlk "github.com/SUSE/container-feeder/walker"
log "github.com/sirupsen/logrus"
)

Expand Down Expand Up @@ -62,7 +59,7 @@ func parseWhitelist(whitelist []string) ([]string, error) {
func loadConfig() (FeederConfig, error) {
config := FeederConfig{}

file, err := ioutil.ReadFile(configFile)
file, err := os.ReadFile(configFile)
if err != nil {
return config, err
}
Expand Down Expand Up @@ -159,6 +156,8 @@ func NewFeeder() (*Feeder, error) {
log.Debugf("Feeder target '%s': using DockerFeeder", f.config.Target)
f.feeder, err = NewDockerFeeder()
case "crio":
fallthrough
case "podman":
log.Debugf("Feeder target '%s': using CRIOFeeder", f.config.Target)
f.feeder, err = NewCRIOFeeder()
default:
Expand Down Expand Up @@ -187,7 +186,7 @@ func Import(path string) (FeederLoadResponse, error) {

log.Debugf("Images to import: %v", imagesToImport)
for tag, file := range imagesToImport {
_, err := f.feeder.LoadImage(file)
loadedName, err := f.feeder.LoadImage(file)
if err != nil {
log.Warnf("Could not load image %s: %v", file, err)
res.FailedImports = append(
Expand All @@ -197,7 +196,7 @@ func Import(path string) (FeederLoadResponse, error) {
Error: err,
})
} else {
err = f.feeder.TagImage(tag, imagesToImportTags[tag])
err = f.feeder.TagImage(loadedName, imagesToImportTags[tag])
if err != nil {
log.Warnf("Could not tag image %s: %v", file, err)
res.FailedImports = append(
Expand All @@ -215,7 +214,7 @@ func Import(path string) (FeederLoadResponse, error) {
return res, nil
}

//normalizeNameTag split the image into it's name and tag.
// normalizeNameTag split the image into it's name and tag.
func normalizeNameTag(image string) (string, string, error) {
// Remove illegal characters when image is "<none>:<none>"
re := regexp.MustCompile(`<|>`)
Expand Down Expand Up @@ -255,7 +254,7 @@ func isWhitelisted(image string, whitelist []string) (bool, error) {

// shouldImportImage will check if any tag under newTags is not inside oldTags.
// If any tag is found that matches this condition, we should import the image.
func (f *Feeder) shouldImportImage(oldTags, newTags []string) (bool) {
func (f *Feeder) shouldImportImage(oldTags, newTags []string) bool {
for _, newTag := range newTags {
if !stringInSlice(newTag, oldTags) {
return true
Expand Down Expand Up @@ -348,7 +347,7 @@ func findRPMImages(path string) (map[string]string, map[string][]string, error)
// file shipped by RPM
// Returns repotag (`<name>:<tag>`), a list of additional tags, and image name
func repotagFromRPMFile(file string) (string, []string, string, error) {
data, err := ioutil.ReadFile(file)
data, err := os.ReadFile(file)
if err != nil {
return "", nil, "", err
}
Expand All @@ -374,24 +373,3 @@ func repotagFromRPMFile(file string) (string, []string, string, error) {

return repotag, repotags, image, nil
}

// runCommand executes the program specified in args with env and writes to
// stdout.
func runCommand(args []string, env string, stdout *os.File) error {
var cmd *exec.Cmd
var serr bytes.Buffer

log.Debugf("runCommand(args=%s, env=%s)", args, env)

cmd = exec.Command(args[0], args[1:]...)
cmd.Stdout = stdout
cmd.Stderr = &serr
cmd.Env = []string{env}

err := cmd.Run()
if err != nil {
log.Debugf("Error executing command: %s", serr.String())
return fmt.Errorf("error running command: %s", err.Error())
}
return nil
}
Loading