Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion actions/install-slsactl/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ runs:
set -x
if [[ "${VERSION}" == "latest" ]]; then
echo 'Checking what the latest version is'
VERSION=$(curl -s 'https://api.github.com/repos/rancherlabs/slsactl/releases/latest' | jq -r '.tag_name')
VERSION=$(curl -s 'https://api.github.com/repos/rancherlabs/slsactl/releases/latest' | jq -r '.tag_name' || echo 'latest')

echo "Using last tag ${VERSION} as version"
fi
Expand Down
4 changes: 2 additions & 2 deletions internal/imagelist/imagelist.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func NewProcessor(registry string) *Processor {
}
}

func (p *Processor) Process(url string) (*Result, error) {
func (p *Processor) Verify(url string) (*Result, error) {
url = strings.TrimSpace(url)
if len(url) == 0 {
return nil, ErrURLCannotBeEmpty
Expand Down Expand Up @@ -88,7 +88,7 @@ func (p *Processor) Process(url string) (*Result, error) {

s.UpdateStatus(image)

entry := p.ip.Process(image)
entry := p.ip.Verify(image)

result.Entries = append(result.Entries, entry)
}
Expand Down
20 changes: 10 additions & 10 deletions internal/imagelist/imagelist_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ func TestProcess(t *testing.T) {
[]byte("image:v1\nimage2:v2\n"),
)), nil)

m.On("Process", "some.registry/image:v1").
m.On("Verify", "some.registry/image:v1").
Return(Entry{Image: "some.registry/image:v1"})
m.On("Process", "some.registry/image2:v2").
m.On("Verify", "some.registry/image2:v2").
Return(Entry{Image: "some.registry/image2:v2"})
},
want: &Result{
Expand All @@ -51,7 +51,7 @@ func TestProcess(t *testing.T) {
[]byte("\n\nimage:v1\n"),
)), nil)

m.On("Process", "some.registry/image:v1").
m.On("Verify", "some.registry/image:v1").
Return(Entry{Image: "some.registry/image:v1"})
},
want: &Result{
Expand All @@ -69,7 +69,7 @@ func TestProcess(t *testing.T) {
[]byte("\n# some:image\nimage:v1\n"),
)), nil)

m.On("Process", "some.registry/image:v1").
m.On("Verify", "some.registry/image:v1").
Return(Entry{Image: "some.registry/image:v1"})
},
want: &Result{
Expand Down Expand Up @@ -98,12 +98,12 @@ func TestProcess(t *testing.T) {
[]byte("image:v1\nimage2:v2\n"),
)), nil)

m.On("Process", "some.registry/image:v1").
m.On("Verify", "some.registry/image:v1").
Return(Entry{
Image: "some.registry/image:v1",
Error: errors.New("image not found"),
})
m.On("Process", "some.registry/image2:v2").
m.On("Verify", "some.registry/image2:v2").
Return(Entry{Image: "some.registry/image2:v2"})
},
want: &Result{
Expand Down Expand Up @@ -138,7 +138,7 @@ func TestProcess(t *testing.T) {
[]byte("docker.io/image:v1\n"),
)), nil)

m.On("Process", "some.registry/image:v1").
m.On("Verify", "some.registry/image:v1").
Return(Entry{Image: "some.registry/image:v1"})
},
want: &Result{
Expand All @@ -154,7 +154,7 @@ func TestProcess(t *testing.T) {
[]byte("registry.com/image:v1\n"),
)), nil)

m.On("Process", "registry.com/image:v1").
m.On("Verify", "registry.com/image:v1").
Return(Entry{Image: "registry.com/image:v1"})
},
want: &Result{
Expand All @@ -173,7 +173,7 @@ func TestProcess(t *testing.T) {
sut.fetcher = m
sut.ip = m

got, err := sut.Process(tc.url)
got, err := sut.Verify(tc.url)

if tc.wantErr == nil {
require.NoError(t, err)
Expand All @@ -192,7 +192,7 @@ type DepsMock struct {
mock.Mock
}

func (m *DepsMock) Process(img string) Entry {
func (m *DepsMock) Verify(img string) Entry {
args := m.Called(img)
return args.Get(0).(Entry)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import (
)

type ImageProcessor interface {
Process(img string) Entry
Verify(img string) Entry
}

type imageVerifier struct {
m sync.Mutex
}

func (i *imageVerifier) Process(img string) Entry {
func (i *imageVerifier) Verify(img string) Entry {
entry := Entry{
Image: img,
}
Expand Down
60 changes: 60 additions & 0 deletions internal/product/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package product

import (
"errors"
"fmt"
"regexp"
"strings"
)

var (
ErrInvalidVersion = errors.New("invalid version")

versionRegex = regexp.MustCompile(`^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(\-(?:alpha|beta|rc)\d+)?$`)

productMapping = map[string]productInfo{
"rancher-prime": {
description: "SUSE Rancher Prime",
imagesUrl: "https://github.com/rancher/rancher/releases/download/%s/rancher-images.txt",
windowsImagesUrl: "https://github.com/rancher/rancher/releases/download/%s/rancher-windows-images.txt",
},
"storage": {
description: "SUSE Storage",
imagesUrl: "https://github.com/longhorn/longhorn/releases/download/%s/longhorn-images.txt",
},
"virtualization": {
description: "SUSE Virtualization",
imagesUrl: "https://github.com/harvester/harvester/releases/download/%s/harvester-images-list-amd64.txt",
},
}
)

type productInfo struct {
description string
imagesUrl string
windowsImagesUrl string
}

type summary struct {
count int
signed int
errors int
}

func product(name, version string) (*productInfo, error) {
if !versionRegex.MatchString(version) {
return nil, fmt.Errorf("%w: %s", ErrInvalidVersion, version)
}

info, found := productMapping[name]
if !found {
var names []string
for name := range productMapping {
names = append(names, name)
}
products := strings.Join(names, ", ")
return nil, fmt.Errorf("product %q not found: options are %s", name, products)
}

return &info, nil
}
63 changes: 9 additions & 54 deletions internal/product/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,64 +2,25 @@ package product

import (
"encoding/json"
"errors"
"fmt"
"log/slog"
"os"
"regexp"
"strings"
"text/tabwriter"

"github.com/rancherlabs/slsactl/internal/imagelist"
)

type productInfo struct {
description string
imagesUrl string
windowsImagesUrl string
}

var (
ErrInvalidVersion = errors.New("invalid version")

versionRegex = regexp.MustCompile(`^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]d*)(\-(?:alpha|beta|rc)\d+)?$`)

productMapping = map[string]productInfo{
"rancher-prime": {
description: "SUSE Rancher Prime",
imagesUrl: "https://github.com/rancher/rancher/releases/download/%s/rancher-images.txt",
windowsImagesUrl: "https://github.com/rancher/rancher/releases/download/%s/rancher-windows-images.txt",
},
"storage": {
description: "SUSE Storage",
imagesUrl: "https://github.com/longhorn/longhorn/releases/download/%s/longhorn-images.txt",
},
"virtualization": {
description: "SUSE Virtualization",
imagesUrl: "https://github.com/harvester/harvester/releases/download/%s/harvester-images-list-amd64.txt",
},
}
)

func Verify(registry, name, version string, summary bool, outputFile bool) error {
if !versionRegex.MatchString(version) {
return fmt.Errorf("%w: %s", ErrInvalidVersion, version)
}

info, found := productMapping[name]
if !found {
var names []string
for name := range productMapping {
names = append(names, name)
}
products := strings.Join(names, ", ")
return fmt.Errorf("product %q not found: options are %s", name, products)
info, err := product(name, version)
if err != nil {
return err
}

fmt.Printf("Verifying container images for %s %s:\n\n", info.description, version)

p := imagelist.NewProcessor(registry)
result, err := p.Process(fmt.Sprintf(info.imagesUrl, version))
result, err := p.Verify(fmt.Sprintf(info.imagesUrl, version))
if err != nil {
return err
}
Expand All @@ -68,7 +29,7 @@ func Verify(registry, name, version string, summary bool, outputFile bool) error
result.Version = version

if len(info.windowsImagesUrl) > 0 {
r2, err := p.Process(fmt.Sprintf(info.windowsImagesUrl, version))
r2, err := p.Verify(fmt.Sprintf(info.windowsImagesUrl, version))
if err == nil {
result.Entries = append(result.Entries, r2.Entries...)
} else {
Expand All @@ -77,26 +38,20 @@ func Verify(registry, name, version string, summary bool, outputFile bool) error
}

if summary {
err = printSummary(result)
err = printVerifySummary(result)
if err != nil {
return fmt.Errorf("failed to print summary: %w", err)
}
}

if outputFile {
return saveOutput(result)
return savePrintOutput(result)
}

return nil
}

type summary struct {
count int
signed int
errors int
}

func printSummary(result *imagelist.Result) error {
func printVerifySummary(result *imagelist.Result) error {
w := new(tabwriter.Writer)
w.Init(os.Stdout, 12, 12, 4, ' ', 0)

Expand Down Expand Up @@ -131,7 +86,7 @@ func printSummary(result *imagelist.Result) error {
return w.Flush()
}

func saveOutput(result *imagelist.Result) error {
func savePrintOutput(result *imagelist.Result) error {
data, err := json.MarshalIndent(result, "", " ")
if err != nil {
return fmt.Errorf("fail to marshal JSON: %w", err)
Expand Down
5 changes: 3 additions & 2 deletions pkg/verify/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"crypto"
"errors"
"fmt"
"os"
"strings"
"time"
Expand All @@ -18,7 +19,7 @@ import (
)

var (
ErrNoVerifierFound = errors.New("no verifier found for this image")
ErrNoVerifierFound = errors.New("no verifier found for image")

cosignVerifier = &cosignImplementation{}

Expand Down Expand Up @@ -64,7 +65,7 @@ func Verify(image string) error {
}
}

return ErrNoVerifierFound
return fmt.Errorf("%w: %q", ErrNoVerifierFound, image)
}

type cosignImplementation struct{}
Expand Down
Loading