Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions pkg/commands/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,11 @@ func makePublisher(po *options.PublishOptions) (publish.Interface, error) {
return publish.NewKindPublisher(namer, po.Tags), nil
}

// handle the k3s distros
if repoName == publish.K3sDomain {
return publish.NewK3sPublisher(namer, po.Tags), nil
}

if repoName == "" && po.Push {
return nil, errors.New("KO_DOCKER_REPO environment variable is unset")
}
Expand Down
89 changes: 89 additions & 0 deletions pkg/publish/k3s.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright 2020 Google LLC All Rights Reserved.
//
// 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 publish

import (
"context"
"fmt"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/ko/pkg/build"
"github.com/google/ko/pkg/publish/k3s"
"log"
"strings"
)

const (
//K3sDomain k3s local sentinel registry where the images get's loaded
K3sDomain = "k3s.local"
)

type k3sPublisher struct {
namer Namer
tags []string
}

//NewK3sPublisher returns a new publish.Interface that loads image into k3s clusters
func NewK3sPublisher(namer Namer, tags []string) Interface {
return &k3sPublisher{
namer: namer,
tags: tags,
}
}

//Publish implements publish.Interface
func (k *k3sPublisher) Publish(ctx context.Context, br build.Result, s string) (name.Reference, error) {
s = strings.TrimPrefix(s, build.StrictScheme)
s = strings.ToLower(s)

img, err := ToImage(br, s)
if err != nil {
return nil, err
}

h, err := img.Digest()
if err != nil {
return nil, err
}

digestTag, err := name.NewTag(fmt.Sprintf("%s:%s", k.namer(K3sDomain, s), h.Hex))
if err != nil {
return nil, err
}

log.Printf("Loading %v", digestTag)
if err := k3s.Write(ctx, digestTag, img); err != nil {
return nil, err
}
log.Printf("Loaded %v", digestTag)

for _, tagName := range k.tags {
tag, err := name.NewTag(fmt.Sprintf("%s:%s", k.namer(K3sDomain, s), tagName))
if err != nil {
return nil, err
}

if err := k3s.Tag(ctx, digestTag, tag); err != nil {
return nil, err
}
log.Printf("Added tag %v", tagName)
}

return &digestTag, nil
}

//Close implements publish.Interface
func (k *k3sPublisher) Close() error {
return nil
}
16 changes: 16 additions & 0 deletions pkg/publish/k3s/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright 2020 Google LLC All Rights Reserved.
//
// 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 k3s defines methods for publishing images into k3s clusters using containerd
package k3s
96 changes: 96 additions & 0 deletions pkg/publish/k3s/write.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright 2020 Google LLC All Rights Reserved.
//
// 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 k3s

import (
"bytes"
"context"
"fmt"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/tarball"
"golang.org/x/sync/errgroup"
"io"
"log"
"os"
"os/exec"
)

const (
limaInstanceEnvKey = "LIMA_INSTANCE"
rancherDesktopLimaInstanceName = "0"
)

// Tag adds a tag to an already existent image.
func Tag(ctx context.Context, src, dest name.Tag) error {
nerdctl, li, env := commandWithEnv()

var stdErr bytes.Buffer
cmd := exec.CommandContext(ctx, nerdctl, "--namespace=k8s.io", "tag", src.String(), dest.String())
cmd.Env = env
cmd.Stderr = &stdErr

if err := cmd.Run(); err != nil {
log.Printf("Error while excuting command %s %s", cmd.String(), stdErr.String())
return fmt.Errorf("failed to tag image to instance %q: %w", li, err)
}

return nil
}

// Write saves the image into the k3s nodes as the given tag.
func Write(ctx context.Context, tag name.Tag, img v1.Image) error {
pr, pw := io.Pipe()

grp := errgroup.Group{}
grp.Go(func() error {
return pw.CloseWithError(tarball.Write(tag, img, pw))
})

nerdctl, li, env := commandWithEnv()

var stdErr bytes.Buffer
cmd := exec.CommandContext(ctx, nerdctl, "--namespace=k8s.io", "load")
cmd.Stdin = pr
cmd.Env = env
cmd.Stderr = &stdErr

if err := cmd.Run(); err != nil {
log.Printf("Error while excuting command %s %s", cmd.String(), stdErr.String())
return fmt.Errorf("failed to load image to instance %q: %w", li, err)
}

if err := grp.Wait(); err != nil {
return fmt.Errorf("failed to write intermediate tarball representation: %w", err)
}

return nil
}

//commandWithEnv build the nerdctl command with correct instance name and required environment variables
func commandWithEnv() (string, string, []string) {
nerdctl := "nerdctl.lima"
env := os.Environ()
// If no LIMA_INSTANCE env is defined it defaults to Rancher Desktop "0"
li, ok := os.LookupEnv(limaInstanceEnvKey)
if !ok {
nerdctl = "nerdctl"
li = rancherDesktopLimaInstanceName
env = append(env,
fmt.Sprintf("LIMA_INSTANCE=%s", li))
}

return nerdctl, li, env
}
43 changes: 3 additions & 40 deletions pkg/publish/kind.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,9 @@ import (
"context"
"fmt"
"log"
"os"
"strings"

"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/ko/pkg/build"
"github.com/google/ko/pkg/publish/kind"
)
Expand Down Expand Up @@ -51,44 +49,9 @@ func (t *kindPublisher) Publish(ctx context.Context, br build.Result, s string)
// https://github.com/google/go-containerregistry/issues/212
s = strings.ToLower(s)

// There's no way to write an index to a kind, so attempt to downcast it to an image.
var img v1.Image
switch i := br.(type) {
case v1.Image:
img = i
case v1.ImageIndex:
im, err := i.IndexManifest()
if err != nil {
return nil, err
}
goos, goarch := os.Getenv("GOOS"), os.Getenv("GOARCH")
if goos == "" {
goos = "linux"
}
if goarch == "" {
goarch = "amd64"
}
for _, manifest := range im.Manifests {
if manifest.Platform == nil {
continue
}
if manifest.Platform.OS != goos {
continue
}
if manifest.Platform.Architecture != goarch {
continue
}
img, err = i.Image(manifest.Digest)
if err != nil {
return nil, err
}
break
}
if img == nil {
return nil, fmt.Errorf("failed to find %s/%s image in index for image: %v", goos, goarch, s)
}
default:
return nil, fmt.Errorf("failed to interpret %s result as image: %v", s, br)
img, err := ToImage(br, s)
if err != nil {
return nil, err
}

h, err := img.Digest()
Expand Down
67 changes: 67 additions & 0 deletions pkg/publish/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright 2020 Google LLC All Rights Reserved.
//
// 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 publish

import (
"fmt"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/ko/pkg/build"
"os"
)

//ToImage builds the image reference from the build result
func ToImage(br build.Result, s string) (v1.Image, error) {
// There's no way to write an index to a kind, so attempt to downcast it to an image.
var img v1.Image
switch i := br.(type) {
case v1.Image:
img = i
case v1.ImageIndex:
im, err := i.IndexManifest()
if err != nil {
return nil, err
}
goos, goarch := os.Getenv("GOOS"), os.Getenv("GOARCH")
if goos == "" {
goos = "linux"
}
if goarch == "" {
goarch = "amd64"
}
for _, manifest := range im.Manifests {
if manifest.Platform == nil {
continue
}
if manifest.Platform.OS != goos {
continue
}
if manifest.Platform.Architecture != goarch {
continue
}
img, err = i.Image(manifest.Digest)
if err != nil {
return nil, err
}
break
}
if img == nil {
return nil, fmt.Errorf("failed to find %s/%s image in index for image: %v", goos, goarch, s)
}
default:
return nil, fmt.Errorf("failed to interpret %s result as image: %v", s, br)
}

return img, nil
}