Skip to content

Commit

Permalink
#246: rancher image check (#265)
Browse files Browse the repository at this point in the history
* add docker package

* add check rancher image command

* rename file

* move url concatenation to docker tag function

* check rancher docker image recieve context

* recieve org repo and archs as arguments

* use constants

* create new yaml decoder for response body

* use rancherOrg

* add http package

* rename to ecmHTTP

* remove bool return

* style fix

---------

Co-authored-by: Pedro Tashima <[email protected]>
  • Loading branch information
tashima42 and tashima42 authored Sep 29, 2023
1 parent 9c58781 commit d3d56dd
Show file tree
Hide file tree
Showing 10 changed files with 593 additions and 56 deletions.
39 changes: 39 additions & 0 deletions cmd/rancher_release/check_rancher_image.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package main

import (
"context"

"github.com/rancher/ecm-distro-tools/release/rancher"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)

const (
rancherOrg = "rancher"
rancherRepo = rancherOrg
)

func checkRancherImageCommand() cli.Command {
return cli.Command{
Name: "check-rancher-image",
Usage: "check if rancher helm charts and docker images exist for a given image tag",
Flags: []cli.Flag{
cli.StringFlag{
Name: "tag",
Usage: "release tag to validate image",
Required: true,
},
},
Action: checkRancherImage,
}
}

func checkRancherImage(c *cli.Context) error {
tag := c.String("tag")
logrus.Debug("tag: " + tag)
rancherArchs := []string{"amd64", "arm64", "s390x"}
if err := rancher.CheckRancherDockerImage(context.Background(), rancherOrg, rancherRepo, tag, rancherArchs); err != nil {
return err
}
return rancher.CheckHelmChartVersion(tag)
}
1 change: 1 addition & 0 deletions cmd/rancher_release/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func main() {
app.Usage = "Perform a Rancher release"
app.Commands = []cli.Command{
listImagesRCCommand(),
checkRancherImageCommand(),
}
app.Flags = rootFlags

Expand Down
71 changes: 71 additions & 0 deletions docker/docker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package docker

import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"time"

ecmHTTP "github.com/rancher/ecm-distro-tools/http"
"github.com/sirupsen/logrus"
)

const registryURL = "https://hub.docker.com"

type DockerImage struct {
Architecture string `json:"architecture"`
Status string `json:"status"`
Size int `json:"size"`
LastPushed time.Time `json:"last_pushed"`
}

type DockerTag struct {
Name string `json:"name"`
Images []DockerImage `json:"images"`
}

// CheckImageArchs checks if an image exists and has all the provided architectures
func CheckImageArchs(ctx context.Context, org, repo, tag string, archs []string) error {
images, err := dockerTag(ctx, org, repo, tag, registryURL)
if err != nil {
return err
}
for _, arch := range archs {
logrus.Info("checking " + arch)
if _, ok := images[arch]; !ok {
return errors.New("arch " + arch + "not found")
}
logrus.Info("passed, " + arch + " exists")
}
return nil
}

// dockerTag returns a map whose keys are the architecture of each image
// or an empty map if the tag is not found.
func dockerTag(ctx context.Context, org, repo, tag, registryURL string) (map[string]DockerImage, error) {
url := registryURL + "/v2/repositories/" + org + "/" + repo + "/tags/" + tag
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, err
}
httpClient := ecmHTTP.NewClient(time.Second * 15)
res, err := httpClient.Do(req)
if err != nil {
return nil, err
}
if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to find docker tag \"%s\", unexpected status code: %d", tag, res.StatusCode)
}
var dt DockerTag
if err := json.NewDecoder(res.Body).Decode(&dt); err != nil {
return nil, err
}

images := make(map[string]DockerImage, len(dt.Images))
for _, image := range dt.Images {
images[image.Architecture] = image
}
return images, nil
}
40 changes: 40 additions & 0 deletions docker/docker_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package docker

import (
"context"
"net/http"
"net/http/httptest"
"reflect"
"testing"
"time"
)

func TestDockerTag(t *testing.T) {
org := "rancher"
repo := "k3s"
tag := "v1.25.14-k3s1"

path := "/v2/repositories/" + org + "/" + repo + "/tags/" + tag
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != path {
t.Errorf("Expected to request '%s', got: %s", path, r.URL.Path)
}
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"creator":11292096,"id":516991909,"images":[{"architecture":"amd64","features":"","variant":null,"digest":"sha256:5d972920146d1fdacb806ffff492cf3d6c6b11ef061e7c9b02345e8cdcc1f817","os":"linux","os_features":"","os_version":null,"size":77338322,"status":"active","last_pulled":"2023-09-27T18:25:01.56539Z","last_pushed":"2023-09-21T02:33:00.168796Z"},{"architecture":"arm64","features":"","variant":null,"digest":"sha256:6a42a300bfd291baa45a8fb87768070a70151e11c5a564148319814d66b84179","os":"linux","os_features":"","os_version":null,"size":70783833,"status":"active","last_pulled":"2023-09-27T17:32:19.147729Z","last_pushed":"2023-09-21T00:54:22.424019Z"},{"architecture":"arm","features":"","variant":null,"digest":"sha256:4a897498d92e55eb2d7f610675c5e03100d79d77be80a476684ead3c34c1c3c7","os":"linux","os_features":"","os_version":null,"size":72527158,"status":"active","last_pulled":"2023-09-27T18:03:46.773767Z","last_pushed":"2023-09-21T00:55:31.424019Z"},{"architecture":"s390x","features":"","variant":null,"digest":"sha256:f19501733d2e07b3ad7957a6b1cfe633c4d5c3bc5ea1a6bd8aabc6cb283b30a0","os":"linux","os_features":"","os_version":null,"size":74795527,"status":"active","last_pulled":"2023-09-27T18:03:48.603478Z","last_pushed":"2023-09-21T00:55:31.381413Z"}],"last_updated":"2023-09-21T02:55:46.86982Z","last_updater":11292096,"last_updater_username":"ks3serviceaccount","name":"v1.25.14-k3s1","repository":6586245,"full_size":77338322,"v2":true,"tag_status":"active","tag_last_pulled":"2023-09-27T18:25:01.56539Z","tag_last_pushed":"2023-09-21T02:55:46.86982Z","media_type":"application/vnd.docker.distribution.manifest.list.v2+json","content_type":"image","digest":"sha256:5f7b660e6f2a6dd712350a8f1fd5ad6beafd62a06df5e2f46e432e6f151652fc"}`))
}))
defer server.Close()

images, err := dockerTag(context.TODO(), org, repo, tag, server.URL)
if err != nil {
t.Error(err)
}
expectedImages := map[string]DockerImage{
"amd64": {Architecture: "amd64", Status: "active", Size: 77338322, LastPushed: time.Date(2023, time.September, 21, 2, 33, 0, 168796000, time.UTC)},
"arm": {Architecture: "arm", Status: "active", Size: 72527158, LastPushed: time.Date(2023, time.September, 21, 0, 55, 31, 424019000, time.UTC)},
"arm64": {Architecture: "arm64", Status: "active", Size: 70783833, LastPushed: time.Date(2023, time.September, 21, 0, 54, 22, 424019000, time.UTC)},
"s390x": {Architecture: "s390x", Status: "active", Size: 74795527, LastPushed: time.Date(2023, time.September, 21, 0, 55, 31, 381413000, time.UTC)},
}
if !reflect.DeepEqual(expectedImages, images) {
t.Errorf("expected:\n %+v\ngot:\n %+v", expectedImages, images)
}
}
36 changes: 19 additions & 17 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ require (
github.com/drone/drone-go v1.7.1
github.com/go-git/go-git/v5 v5.4.2
github.com/google/go-github/v39 v39.2.0
github.com/sirupsen/logrus v1.8.1
go.opentelemetry.io/otel v1.8.0
go.opentelemetry.io/otel/sdk v1.8.0
go.opentelemetry.io/otel/trace v1.8.0
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
golang.org/x/mod v0.3.0
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
github.com/sirupsen/logrus v1.9.0
go.opentelemetry.io/otel v1.10.0
go.opentelemetry.io/otel/sdk v1.10.0
go.opentelemetry.io/otel/trace v1.10.0
golang.org/x/crypto v0.5.0
golang.org/x/mod v0.8.0
golang.org/x/oauth2 v0.1.0
)

require (
Expand All @@ -22,31 +22,33 @@ require (
)

require (
github.com/Microsoft/go-winio v0.4.16 // indirect
github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
github.com/acomagu/bufpipe v1.0.3 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/emirpasic/gods v1.12.0 // indirect
github.com/go-git/gcfg v1.5.0 // indirect
github.com/go-git/go-billy/v5 v5.3.1 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 // indirect
github.com/russross/blackfriday/v2 v2.0.1 // indirect
github.com/rogpeppe/go-internal v1.10.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/stretchr/testify v1.8.1 // indirect
github.com/xanzy/ssh-agent v0.3.0 // indirect
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 // indirect
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/sys v0.6.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.26.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading

0 comments on commit d3d56dd

Please sign in to comment.