Skip to content

Commit

Permalink
Implement KubeVirt upload
Browse files Browse the repository at this point in the history
Signed-off-by: Roman Mohr <[email protected]>
  • Loading branch information
rmohr committed Mar 11, 2022
1 parent fb97a30 commit ef90ea4
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 6 deletions.
5 changes: 5 additions & 0 deletions docs/mantle/credentials.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,11 @@ for more information about the `.boto` file.

`qemu-unpriv` is run locally and needs no credentials. It has a restricted set of functionality compared to the `qemu` platform, such as:

## kubevirt

`kubevirt` publishes a containerdisk which can be consumed by KubeVirt. In order to publish the conatinerdisk, the
credentials to the container registry need to be provided in `~/.docker/config.json`.

- No [Local cluster](platform/local/)
- Usermode networking instead of namespaced networks
* Single node only, no machine to machine networking
Expand Down
21 changes: 21 additions & 0 deletions mantle/cmd/ore/kubevirt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2021 Red Hat
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import "github.com/coreos/mantle/cmd/ore/kubevirt"

func init() {
root.AddCommand(kubevirt.KubeVirt)
}
104 changes: 104 additions & 0 deletions mantle/cmd/ore/kubevirt/build.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package kubevirt

import (
"archive/tar"
"fmt"
"io"
"os"
"time"

v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/empty"
"github.com/google/go-containerregistry/pkg/v1/mutate"
"github.com/google/go-containerregistry/pkg/v1/tarball"
)

func BuildContainerDisk(imgPath string) (v1.Image, error) {
img := empty.Image
layerStream, errChan := StreamLayer(imgPath)
layer, err := tarball.LayerFromReader(layerStream)
if err != nil {
return nil, fmt.Errorf("error creating an image layer from disk: %v", err)
}

img, err = mutate.AppendLayers(img, layer)
if err != nil {
return nil, fmt.Errorf("error appending the image layer: %v", err)
}

if err := <-errChan; err != nil {
return nil, fmt.Errorf("error creating the tar file with the disk: %v", err)
}
return img, nil
}

func StreamLayer(imagePath string) (tarReader io.ReadCloser, errChan chan error) {
errChan = make(chan error, 1)
reader, writer := io.Pipe()
tarWriter := tar.NewWriter(writer)

go func() {
defer writer.Close()
defer tarWriter.Close()
err := addFileToTarWriter(imagePath, tarWriter)
if err != nil {
errChan <- fmt.Errorf("error adding file '%s', to tarball: %v", imagePath, err)
}
close(errChan)
}()

return reader, errChan
}

func addFileToTarWriter(filePath string, tarWriter *tar.Writer) error {
file, err := os.Open(filePath)
if err != nil {
return fmt.Errorf("error opening file: %v", err)
}
defer file.Close()

stat, err := file.Stat()
if err != nil {
return fmt.Errorf("error getting file information with stat: %v", err)
}

header := &tar.Header{
Typeflag: tar.TypeDir,
Name: "disk/",
Mode: 0555,
Uid: 107,
Gid: 107,
Uname: "qemu",
Gname: "qemu",
ModTime: time.Now(),
}

err = tarWriter.WriteHeader(header)
if err != nil {
return fmt.Errorf("error writing disks directory tar header: %v", err)
}

header = &tar.Header{
Typeflag: tar.TypeReg,
Uid: 107,
Gid: 107,
Uname: "qemu",
Gname: "qemu",
Name: "disk/disk.img",
Size: stat.Size(),
Mode: 0444,
ModTime: stat.ModTime(),
}

err = tarWriter.WriteHeader(header)
if err != nil {
return fmt.Errorf("error writing image file tar header: %v", err)
}

_, err = io.Copy(tarWriter, file)
if err != nil {
return fmt.Errorf("error writingfile into tarball: %v", err)
}

return nil
}
68 changes: 68 additions & 0 deletions mantle/cmd/ore/kubevirt/publish.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package kubevirt

import (
"context"
"fmt"
"github.com/coreos/mantle/cli"
"github.com/coreos/pkg/capnslog"
"github.com/spf13/cobra"
)

type Options struct {
Image string
Tags []string
Repository string
DryRun bool
}

var (
plog = capnslog.NewPackageLogger("github.com/coreos/mantle", "ore/kubevirt")

KubeVirt = &cobra.Command{
Use: "kubevirt",
Short: "kubevirt publish",
}
options = &Options{
DryRun: true,
}

publishCmd = &cobra.Command{
Use: "publish",
RunE: func(cmd *cobra.Command, args []string) error {
return buildAndPublish(options)
},
}
)

func init() {
KubeVirt.AddCommand(publishCmd)
publishCmd.Flags().StringVar(&options.Image, "image", "", "Path to the qcow2 image to publish")
publishCmd.Flags().StringArrayVar(&options.Tags, "tag", options.Tags, "Container image tags")
publishCmd.Flags().StringVar(&options.Repository, "repository", options.Repository, "Container image repository")
publishCmd.Flags().BoolVar(&options.DryRun, "dry-run", options.DryRun, "dry run")
publishCmd.MarkFlagRequired("image")
publishCmd.MarkFlagRequired("tag")
publishCmd.MarkFlagRequired("repository")
cli.WrapPreRun(KubeVirt, preflightCheck)
}

func buildAndPublish(options *Options) error {
plog.Infof("Building containerDisk for %s", options.Image)
containerDisk, err := BuildContainerDisk(options.Image)
if err != nil {
return fmt.Errorf("error creating the containerdisk : %v", err)
}
for _, tag := range options.Tags {
containerName := fmt.Sprintf("%s:%s", options.Repository, tag)
plog.Infof("Pushing containerDisk %q", containerName)
if !options.DryRun {
if err := PushImage(context.Background(), containerDisk, containerName); err != nil {
return err
}
}
}
return nil
}
func preflightCheck(cmd *cobra.Command, args []string) error {
return nil
}
15 changes: 15 additions & 0 deletions mantle/cmd/ore/kubevirt/push.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package kubevirt

import (
"context"
"fmt"
"github.com/google/go-containerregistry/pkg/crane"
v1 "github.com/google/go-containerregistry/pkg/v1"
)

func PushImage(ctx context.Context, img v1.Image, name string) error {
if err := crane.Push(img, name, crane.WithContext(ctx)); err != nil {
return fmt.Errorf("error pushing image %q: %v", img, err)
}
return nil
}
37 changes: 31 additions & 6 deletions src/cosalib/kubevirt.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,29 @@
import os.path

from cosalib.cmdlib import (
run_verbose
)


def kubevirt_run_ore(build, args):
print("""
Images are not published to KubeVirt. This command is a placeholder.
""")
ore_args = ['ore']
if args.log_level:
ore_args.extend(['--log-level', args.log_level])

name = f"{build.build_name}"
if args.name is not None:
name = args.name
tag = f"{build.build_id}-{build.basearch}"
full_name = os.path.join(args.repository, name)
ore_args.extend([
'kubevirt', 'publish',
'--image', f"{build.image_path}",
'--tag', tag,
'--repository', f"{full_name}",
])
if not args.dry_run:
ore_args.extend(['--dry-run', 'false'])
run_verbose(ore_args)


def kubevirt_run_ore_replicate(*args, **kwargs):
Expand All @@ -12,7 +34,10 @@ def kubevirt_run_ore_replicate(*args, **kwargs):


def kubevirt_cli(parser):
"""
Extend a parser with the KubeVirt options
"""
parser.add_argument("--name",
help="Name to append to the repository (e.g. fedora-coreos). Defaults to the build name.")
parser.add_argument("--repository", help="repository to push to (e.g. quay.io or quay.io/myorg)", required=True)
parser.add_argument('--dry-run', dest='dry_run', action='store_true')
parser.add_argument('--no-dry-run', dest='dry_run', action='store_false')
parser.set_defaults(dry_run=True)
return parser

0 comments on commit ef90ea4

Please sign in to comment.