Skip to content
This repository has been archived by the owner on Feb 24, 2020. It is now read-only.

add support to import image from docker-daemon in rkt #3939

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
97 changes: 97 additions & 0 deletions pkg/distribution/docker-daemon.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Copyright 2018 The rkt Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package distribution

import (
"fmt"
"net/url"

d2acommon "github.com/appc/docker2aci/lib/common"
)

const (
TypeDockerDaemon Type = "docker-daemon"
)

func init() {
Register(TypeDockerDaemon, NewDockerDaemon)
}

type DockerDaemon struct {
url string // a valid docker reference URL
parsedURL *d2acommon.ParsedDockerURL

full string // the full string representation for equals operations
simple string // the user friendly (simple) string representation
}

func NewDockerDaemon(u *url.URL) (Distribution, error) {
dp, err := parseCIMD(u)
if err != nil {
return nil, fmt.Errorf("cannot parse URI: %q: %v", u.String(), err)
}
if dp.Type != TypeDocker {
return nil, fmt.Errorf("wrong distribution type: %q", dp.Type)
}

parsed, err := d2acommon.ParseDockerURL(dp.Data)
if err != nil {
return nil, fmt.Errorf("bad docker URL %q: %v", dp.Data, err)
}

return &DockerDaemon{
url: dp.Data,
parsedURL: parsed,
simple: SimpleDockerRef(parsed),
full: FullDockerRef(parsed),
}, nil
}

func NewDockerDaemonFromString(ds string) (Distribution, error) {
urlStr := NewCIMDString(TypeDocker, distDockerVersion, ds)
u, err := url.Parse(urlStr)
if err != nil {
return nil, err
}
return NewDockerDaemon(u)
}

func (d *DockerDaemon) CIMD() *url.URL {
uriStr := NewCIMDString(TypeDocker, distDockerVersion, d.url)
// Create a copy of the URL
u, err := url.Parse(uriStr)
if err != nil {
panic(err)
}
return u
}

func (d *DockerDaemon) String() string {
return d.simple
}

func (d *DockerDaemon) Equals(dist Distribution) bool {
d2, ok := dist.(*DockerDaemon)
if !ok {
return false
}

return d.full == d2.full
}

// ReferenceURL returns the docker reference URL.
func (d *DockerDaemon) ReferenceURL() string {
return d.url
}
14 changes: 14 additions & 0 deletions rkt/image/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,20 @@ func DistFromImageString(is string) (dist.Distribution, error) {
return nil, fmt.Errorf("docker distribution creation error: %v", err)
}
return dist, nil
case "docker-daemon":
dockerDaemonStr := is
if strings.HasPrefix(dockerDaemonStr, "docker-daemon://") {
dockerDaemonStr = strings.TrimPrefix(dockerDaemonStr, "docker-daemon://")
} else if strings.HasPrefix(dockerDaemonStr, "docker-daemon:") {
dockerDaemonStr = strings.TrimPrefix(dockerDaemonStr, "docker-daemon:")
}

dist, err := dist.NewDockerDaemonFromString(dockerDaemonStr)
if err != nil {
return nil, fmt.Errorf("docker-daemon distribution creation error: %v", err)
}
return dist, nil

case dist.Scheme: // cimd
return dist.Parse(is)
default:
Expand Down
126 changes: 126 additions & 0 deletions rkt/image/docker-daemon-fetcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// Copyright 2018 The rkt Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package image

import (
"context"
"fmt"
"io"
"os"

docker2aci "github.com/appc/docker2aci/lib"
d2acommon "github.com/appc/docker2aci/lib/common"
dockerCli "github.com/docker/docker/client"
)

type dockerDaemonFetcher struct {
*Fetcher
ImageName string
}

func (d *dockerDaemonFetcher) Hash() (string, error) {
// image verification is not supported for docker images so adding this check
// incase the user has forgotten to give such a flag then indicate such
if !d.InsecureFlags.SkipImageCheck() {
return "", fmt.Errorf("signature verification for docker images is not supported (try --insecure-options=image)")
}

// fetch the image from docker's store as tar file
tarFilePath, tarCleaner, err := d.dockerToTar()
if err != nil {
return "", err
}
defer tarCleaner()

// convert the tar file into rkt readable ACI format
aciFilePath, aciCleaner, err := d.tarToACI(tarFilePath)
if err != nil {
return "", err
}
defer aciCleaner()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aciCleaner() called twice? (line 45 and 52)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops, I can't read. ignore me


// now that we have the ACI format image import into rkt store
return d.importToRktStore(aciFilePath)
}

// dockerToTar fetches the image from docker's store and save it as
// a tar file in temporary place
func (d *dockerDaemonFetcher) dockerToTar() (string, func(), error) {
// create a docker client to interact with docker daemon
cli, err := dockerCli.NewEnvClient()
if err != nil {
return "", nil, fmt.Errorf("creating the docker client: %v", err)
}

// fetch the image from docker's store
tar, err := cli.ImageSave(
context.Background(),
[]string{d.ImageName},
)
if err != nil {
return "", nil, fmt.Errorf("fetching the image from docker store: %v", err)
}
defer tar.Close()

// create a temporary file to copy the tar data we just received
tmpTarFile, err := d.S.TmpFile()
if err != nil {
return "", nil, fmt.Errorf("creating tar file: %v", err)
}
defer tmpTarFile.Close()

// now copy that tar content into a temporary file
if _, err = io.Copy(tmpTarFile, tar); err != nil {
return "", nil, fmt.Errorf("copying to tar file: %v", err)
}
tmpTarFile.Close()

path := tmpTarFile.Name()
return path, func() {
os.Remove(path)
}, nil
}

// tarToACI converts the tar file fetched from docker's store into
// rkt readable ACI image format
func (d *dockerDaemonFetcher) tarToACI(tarFilePath string) (string, func(), error) {
// we will save all the temporary artifacts in this directory
tempDir, err := d.S.TmpDir()
if err != nil {
return "", nil, fmt.Errorf("creating temporary directory: %v", err)
}

// Now convert that tar file into aci
out, err := docker2aci.ConvertSavedFile(tarFilePath, docker2aci.FileConfig{
CommonConfig: docker2aci.CommonConfig{
Squash: true,
OutputDir: tempDir,
TmpDir: tempDir,
Compression: d2acommon.GzipCompression,
},
})
if err != nil {
return "", nil, fmt.Errorf("converting tar to aci: %v", err)
}

return out[0], func() {
os.RemoveAll(tempDir)
}, nil
}

func (d *dockerDaemonFetcher) importToRktStore(aciFilePath string) (string, error) {
// TODO: implement the way to handle the image security options
return d.fetchSingleImageByPath(aciFilePath, nil)
}
10 changes: 10 additions & 0 deletions rkt/image/fetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ func (f *Fetcher) fetchSingleImage(db *distBundle, a *asc) (string, error) {
return f.fetchSingleImageByName(db, a)
case *dist.Docker:
return f.fetchSingleImageByDockerURL(v)
case *dist.DockerDaemon:
return f.fetchSingleImageByDockerDaemonURL(v)
default:
return "", fmt.Errorf("unknown distribution type %T", v)
}
Expand Down Expand Up @@ -231,6 +233,14 @@ func (f *Fetcher) fetchSingleImageByDockerURL(d *dist.Docker) (string, error) {
return "", fmt.Errorf("unable to fetch docker image from URL %q: either image was not found in the store or store was disabled and fetching from remote yielded nothing or it was disabled", u.String())
}

func (f *Fetcher) fetchSingleImageByDockerDaemonURL(d *dist.DockerDaemon) (string, error) {
ddf := &dockerDaemonFetcher{
Fetcher: f,
ImageName: d.ReferenceURL(),
}
return ddf.Hash()
}

func (f *Fetcher) maybeCheckRemoteFromStore(rem *imagestore.Remote) string {
if f.PullPolicy == PullPolicyUpdate || rem == nil {
return ""
Expand Down