-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Brooks Newberry <[email protected]>
- Loading branch information
Showing
12 changed files
with
979 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
package cmd | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"io" | ||
"os" | ||
"sort" | ||
"strings" | ||
"text/tabwriter" | ||
|
||
"github.com/google/go-containerregistry/pkg/name" | ||
reg "github.com/rancher/ecm-distro-tools/registry" | ||
"github.com/rancher/ecm-distro-tools/release" | ||
"github.com/rancher/ecm-distro-tools/release/rke2" | ||
"github.com/rancher/ecm-distro-tools/repository" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
const ( | ||
ossRegistry = "docker.io" | ||
) | ||
|
||
func archStatus(expected bool, ossInfo, primeInfo reg.Image, platform reg.Platform) string { | ||
if !expected { | ||
return "-" | ||
} | ||
|
||
hasArch := ossInfo.Platforms[platform] && primeInfo.Platforms[platform] | ||
if hasArch { | ||
return "✓" | ||
} | ||
return "✗" | ||
} | ||
|
||
func windowsStatus(expected, exists bool) string { | ||
if !expected { | ||
return "-" | ||
} | ||
if exists { | ||
return "✓" | ||
} | ||
return "✗" | ||
} | ||
|
||
func formatImageRef(ref name.Reference) string { | ||
return ref.Context().RepositoryStr() + ":" + ref.Identifier() | ||
} | ||
|
||
func table(w io.Writer, results []rke2.Image) { | ||
sort.Slice(results, func(i, j int) bool { | ||
return formatImageRef(results[i].Reference) < formatImageRef(results[j].Reference) | ||
}) | ||
|
||
missingCount := 0 | ||
for _, result := range results { | ||
if !result.OSSImage.Exists || !result.PrimeImage.Exists { | ||
missingCount++ | ||
} | ||
} | ||
if missingCount > 0 { | ||
fmt.Fprintln(w, missingCount, "incomplete images") | ||
} else { | ||
fmt.Fprintln(w, "all images OK") | ||
} | ||
|
||
tw := tabwriter.NewWriter(w, 0, 8, 2, ' ', 0) | ||
defer tw.Flush() | ||
|
||
fmt.Fprintln(tw, "image\toss\tprime\tsig\tamd64\tarm64\twin") | ||
fmt.Fprintln(tw, "-----\t---\t-----\t---\t-----\t-----\t-------") | ||
|
||
for _, result := range results { | ||
ossStatus := "✗" | ||
if result.OSSImage.Exists { | ||
ossStatus = "✓" | ||
} | ||
primeStatus := "✗" | ||
if result.PrimeImage.Exists { | ||
primeStatus = "✓" | ||
} | ||
tw.Write([]byte(strings.Join([]string{ | ||
formatImageRef(result.Reference), | ||
ossStatus, | ||
primeStatus, | ||
"?", // sigstore not implemented | ||
archStatus(result.ExpectsLinuxAmd64, result.OSSImage, result.PrimeImage, reg.Platform{OS: "linux", Architecture: "amd64"}), | ||
archStatus(result.ExpectsLinuxArm64, result.OSSImage, result.PrimeImage, reg.Platform{OS: "linux", Architecture: "arm64"}), | ||
windowsStatus(result.ExpectsWindows, result.OSSImage.Exists && result.PrimeImage.Exists), | ||
"", | ||
}, "\t") + "\n")) | ||
} | ||
} | ||
|
||
func csv(w io.Writer, results []rke2.Image) { | ||
sort.Slice(results, func(i, j int) bool { | ||
return formatImageRef(results[i].Reference) < formatImageRef(results[j].Reference) | ||
}) | ||
|
||
fmt.Fprintln(w, "image,oss,prime,sig,amd64,arm64,win") | ||
|
||
for _, result := range results { | ||
ossStatus := "N" | ||
if result.OSSImage.Exists { | ||
ossStatus = "Y" | ||
} | ||
primeStatus := "N" | ||
if result.PrimeImage.Exists { | ||
primeStatus = "Y" | ||
} | ||
|
||
amd64Status := "" | ||
if result.ExpectsLinuxAmd64 { | ||
if result.OSSImage.Platforms[reg.Platform{OS: "linux", Architecture: "amd64"}] && | ||
result.PrimeImage.Platforms[reg.Platform{OS: "linux", Architecture: "amd64"}] { | ||
amd64Status = "Y" | ||
} else { | ||
amd64Status = "N" | ||
} | ||
} | ||
|
||
arm64Status := "" | ||
if result.ExpectsLinuxArm64 { | ||
if result.OSSImage.Platforms[reg.Platform{OS: "linux", Architecture: "arm64"}] && | ||
result.PrimeImage.Platforms[reg.Platform{OS: "linux", Architecture: "arm64"}] { | ||
arm64Status = "Y" | ||
} else { | ||
arm64Status = "N" | ||
} | ||
} | ||
|
||
winStatus := "" | ||
if result.ExpectsWindows { | ||
if result.OSSImage.Exists && result.PrimeImage.Exists { | ||
winStatus = "Y" | ||
} else { | ||
winStatus = "N" | ||
} | ||
} | ||
|
||
values := []string{ | ||
formatImageRef(result.Reference), | ||
ossStatus, | ||
primeStatus, | ||
"?", // sigstore not implemented | ||
amd64Status, | ||
arm64Status, | ||
winStatus, | ||
} | ||
fmt.Fprintln(w, strings.Join(values, ",")) | ||
} | ||
} | ||
|
||
var inspectCmd = &cobra.Command{ | ||
Use: "inspect [version]", | ||
Short: "Inspect release artifacts", | ||
Long: `Inspect release artifacts for a given version. | ||
Currently supports inspecting the image list for published rke2 releases.`, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
if len(args) < 1 { | ||
return errors.New("expected at least one argument: [version]") | ||
} | ||
|
||
ctx := context.Background() | ||
gh := repository.NewGithub(ctx, rootConfig.Auth.GithubToken) | ||
filesystem, err := release.NewFS(ctx, gh, "rancher", "rke2", args[0]) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
ossClient := reg.NewClient(ossRegistry, debug) | ||
|
||
var primeClient *reg.Client | ||
if rootConfig.PrimeRegistry != "" { | ||
primeClient = reg.NewClient(rootConfig.PrimeRegistry, debug) | ||
} | ||
|
||
inspector := rke2.NewReleaseInspector(filesystem, ossClient, primeClient, debug) | ||
|
||
results, err := inspector.InspectRelease(ctx, args[0]) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
outputFormat, _ := cmd.Flags().GetString("output") | ||
switch outputFormat { | ||
case "csv": | ||
csv(os.Stdout, results) | ||
default: | ||
table(os.Stdout, results) | ||
} | ||
|
||
return nil | ||
}, | ||
} | ||
|
||
func init() { | ||
rootCmd.AddCommand(inspectCmd) | ||
inspectCmd.Flags().StringP("output", "o", "table", "Output format (table|csv)") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package cmd | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"io/fs" | ||
"os" | ||
"testing" | ||
"testing/fstest" | ||
|
||
"github.com/google/go-containerregistry/pkg/name" | ||
reg "github.com/rancher/ecm-distro-tools/registry" | ||
"github.com/rancher/ecm-distro-tools/release/rke2" | ||
) | ||
|
||
type mockRegistryClient struct { | ||
images map[string]reg.Image | ||
} | ||
|
||
func (m *mockRegistryClient) Image(_ context.Context, ref name.Reference) (reg.Image, error) { | ||
key := ref.Context().RepositoryStr() + ":" + ref.Identifier() | ||
if img, ok := m.images[key]; ok { | ||
return img, nil | ||
} | ||
return reg.Image{Exists: false, Platforms: make(map[reg.Platform]bool)}, nil | ||
} | ||
|
||
func newMockFS() fs.FS { | ||
return fstest.MapFS{ | ||
"rke2-images-all.linux-amd64.txt": &fstest.MapFile{ | ||
Data: []byte("rancher/rke2-runtime:v1.23.4-rke2r1\nrancher/rke2-cloud-provider:v1.23.4-rke2r1"), | ||
}, | ||
"rke2-images-all.linux-arm64.txt": &fstest.MapFile{ | ||
Data: []byte("rancher/rke2-runtime:v1.23.4-rke2r1"), | ||
}, | ||
"rke2-images.windows-amd64.txt": &fstest.MapFile{ | ||
Data: []byte("rancher/rke2-runtime-windows:v1.23.4-rke2r1"), | ||
}, | ||
} | ||
} | ||
|
||
func TestInspectAndCSVOutput(t *testing.T) { | ||
ossImages := map[string]reg.Image{ | ||
"rancher/rke2-runtime:v1.23.4-rke2r1": { | ||
Exists: true, | ||
Platforms: map[reg.Platform]bool{ | ||
{OS: "linux", Architecture: "amd64"}: true, | ||
{OS: "linux", Architecture: "arm64"}: true, | ||
}, | ||
}, | ||
"rancher/rke2-cloud-provider:v1.23.4-rke2r1": { | ||
Exists: true, | ||
Platforms: map[reg.Platform]bool{ | ||
{OS: "linux", Architecture: "amd64"}: true, | ||
}, | ||
}, | ||
} | ||
|
||
primeImages := map[string]reg.Image{ | ||
"rancher/rke2-runtime:v1.23.4-rke2r1": { | ||
Exists: true, | ||
Platforms: map[reg.Platform]bool{ | ||
{OS: "linux", Architecture: "amd64"}: true, | ||
{OS: "linux", Architecture: "arm64"}: true, | ||
}, | ||
}, | ||
"rancher/rke2-cloud-provider:v1.23.4-rke2r1": { | ||
Exists: false, | ||
Platforms: map[reg.Platform]bool{ | ||
{OS: "linux", Architecture: "amd64"}: true, | ||
}, | ||
}, | ||
} | ||
|
||
inspector := rke2.NewReleaseInspector( | ||
newMockFS(), | ||
&mockRegistryClient{images: ossImages}, | ||
&mockRegistryClient{images: primeImages}, | ||
false, | ||
) | ||
|
||
results, err := inspector.InspectRelease(context.Background(), "v1.23.4+rke2r1") | ||
if err != nil { | ||
t.Fatalf("InspectRelease() error = %v", err) | ||
} | ||
|
||
var buf bytes.Buffer | ||
csv(&buf, results) | ||
|
||
expectedBytes, err := os.ReadFile("testdata/inspect_test_output.csv") | ||
if err != nil { | ||
t.Fatalf("failed to read test data: %v", err) | ||
} | ||
expected := string(expectedBytes) | ||
if got := buf.String(); got != expected { | ||
t.Errorf("csv() output = %q, want %q", got, expected) | ||
} | ||
} | ||
|
||
func mustParseRef(s string) name.Reference { | ||
ref, err := name.ParseReference(s) | ||
if err != nil { | ||
panic(err) | ||
} | ||
return ref | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
image,oss,prime,sig,amd64,arm64,win | ||
rancher/rke2-cloud-provider:v1.23.4-rke2r1,Y,N,?,Y,, | ||
rancher/rke2-runtime-windows:v1.23.4-rke2r1,N,N,?,,,N | ||
rancher/rke2-runtime:v1.23.4-rke2r1,Y,Y,?,Y,Y, |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.