Skip to content

Commit

Permalink
feat: validate app name is consistent with pkg name (#168)
Browse files Browse the repository at this point in the history
* feat: validate app name is consistent with pkg name

* improve error messages
  • Loading branch information
rubenruizdegauna authored Feb 19, 2025
1 parent 0f6a188 commit c8c99d6
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 10 deletions.
57 changes: 56 additions & 1 deletion publisher/config/schema.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
package config

import (
"errors"
"fmt"
"gopkg.in/yaml.v2"
"io/ioutil"
"strings"
)

//Errors
// Errors
const (
noDestinationError = "no uploads were provided for the schema"
//FileTypes
TypeFile = "file"
TypeZypp = "zypp"
TypeYum = "yum"
TypeApt = "apt"
)

var fileTypes = []string{TypeFile, TypeZypp, TypeYum, TypeApt}

// Define specific error types
var (
ErrInvalidAppName = errors.New("invalid app name")
ErrInvalidType = errors.New("invalid upload type")
)

type UploadArtifactSchema struct {
Expand Down Expand Up @@ -65,3 +80,43 @@ func parseUploadSchema(fileContent []byte) (UploadArtifactSchemas, error) {

return schema, nil
}

// ValidateSchemas validates that some components of the schema are correct (this is still wip)
// It validates:
// - no incorrect fileType is present
// - the app name is the prefix of the src package. This is mandatory to create consistent apt repositories.
// If they are not equal, the file will be uploaded to a location, and the metadata will point to different location
// which will break the apt repository as you'll receive a 404 when trying to install the package.
func ValidateSchemas(appName string, schemas UploadArtifactSchemas) error {
for _, schema := range schemas {
if err := validateName(appName, schema.Src); err != nil {
return fmt.Errorf("invalid app name %s for schema %s: %w", appName, schema.Src, err)
}
for _, upload := range schema.Uploads {
if err := validateType(upload.Type); err != nil {
return fmt.Errorf("invalid uploadType %s for schema %s err: %w", upload.Type, schema.Src, err)
}
}
}
return nil
}

// validateType checks if the upload type is in the list of valid types fileTypes
func validateType(uploadType string) error {
for _, validType := range fileTypes {
if uploadType == validType {
return nil
}
}
return fmt.Errorf("%w: '%s' (valid types: %s)", ErrInvalidType, uploadType, strings.Join(fileTypes, ", "))
}

func validateName(appName string, src string) error {
if appName == "" {
return fmt.Errorf("%w: appName cannot be empty", ErrInvalidAppName)
}
if !strings.HasPrefix(src, appName) {
return fmt.Errorf("%w: %s should prefix %s", ErrInvalidAppName, appName, src)
}
return nil
}
78 changes: 77 additions & 1 deletion publisher/config/schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package config

import (
"errors"
"fmt"
"github.com/stretchr/testify/assert"
"log"
"testing"
Expand Down Expand Up @@ -127,7 +128,8 @@ func TestSchema(t *testing.T) {
{"invalid yaml schema", "../../test/schemas/bad-formatted-yaml.yml", errors.New("yaml: line 27: mapping values are not allowed in this context")},
}

for _, tt := range tests {
for i := range tests {
tt := tests[i]
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
uploadSchema, err := ParseUploadSchemasFile(tt.schemaPath)
Expand All @@ -136,3 +138,77 @@ func TestSchema(t *testing.T) {
})
}
}

func TestValidateSchemas(t *testing.T) {
tests := []struct {
name string
appName string
schemas UploadArtifactSchemas
expectedError error
}{
{name: "valid app name with pkg suffix", appName: "some-app-name", schemas: UploadArtifactSchemas{{Src: "some-app-name_some_suffix_0.0.1_amd64.deb", Uploads: []Upload{{Type: TypeApt}}}}, expectedError: nil},
{name: "valid app name exactly as pkg name", appName: "some-app-name", schemas: UploadArtifactSchemas{{Src: "some-app-name_0.0.1_amd64.deb", Uploads: []Upload{{Type: TypeApt}}}}, expectedError: nil},
{name: "invalid app name", appName: "some-app-name", schemas: UploadArtifactSchemas{{Src: "some-other-name_0.0.1_amd64.deb", Uploads: []Upload{{Type: TypeFile}}}}, expectedError: ErrInvalidAppName},
{name: "valid apt", appName: "some-app-name", schemas: UploadArtifactSchemas{{Src: "some-app-name_0.0.1_amd64.deb", Uploads: []Upload{{Type: TypeApt}}}}, expectedError: nil},
{name: "valid yum", appName: "some-app-name", schemas: UploadArtifactSchemas{{Src: "some-app-name_0.0.1_amd64.rpm", Uploads: []Upload{{Type: TypeYum}}}}, expectedError: nil},
{name: "valid file", appName: "some-app-name", schemas: UploadArtifactSchemas{{Src: "some-app-name_0.0.1_amd64.tar-gz", Uploads: []Upload{{Type: TypeFile}}}}, expectedError: nil},
{name: "valid zypp", appName: "some-app-name", schemas: UploadArtifactSchemas{{Src: "some-app-name_0.0.1_amd64.rpm", Uploads: []Upload{{Type: TypeZypp}}}}, expectedError: nil},
{name: "invalid type even with valid file", appName: "some-app-name", schemas: UploadArtifactSchemas{{Src: "some-app-name_0.0.1_amd64.deb", Uploads: []Upload{{Type: "something wrong"}}}}, expectedError: ErrInvalidType},
}

for i := range tests {
tt := tests[i]
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
err := ValidateSchemas(tt.appName, tt.schemas)
assert.ErrorIs(t, err, tt.expectedError)
})
}
}

func Test_ValidTypes(t *testing.T) {
tests := []struct {
name string
uploadType string
expectedError error
}{
{name: "valid apt", uploadType: TypeApt, expectedError: nil},
{name: "valid yum", uploadType: TypeYum, expectedError: nil},
{name: "valid file", uploadType: TypeFile, expectedError: nil},
{name: "valid zypp", uploadType: TypeZypp, expectedError: nil},
{name: "invalid empty", uploadType: "", expectedError: fmt.Errorf("%w: '' (valid types: file, zypp, yum, apt)", ErrInvalidType)},
{name: "invalid type", uploadType: "something wrong", expectedError: fmt.Errorf("%w: 'something wrong' (valid types: file, zypp, yum, apt)", ErrInvalidType)},
}

for i := range tests {
tt := tests[i]
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
err := validateType(tt.uploadType)
assert.Equal(t, tt.expectedError, err)
})
}
}

func Test_ValidateName(t *testing.T) {
tests := []struct {
name string
appName string
src string
expectedError error
}{
{name: "empty appName is invalid", appName: "", src: "some-app-name_some_suffix_0.0.1_amd64.deb", expectedError: fmt.Errorf("%w: appName cannot be empty", ErrInvalidAppName)},
{name: "not matching prefix is invalid", appName: "other-app-name", src: "some-app-name_some_suffix_0.0.1_amd64.deb", expectedError: fmt.Errorf("%w: other-app-name should prefix some-app-name_some_suffix_0.0.1_amd64.deb", ErrInvalidAppName)},
{name: "matching prefix is valid", appName: "some-app-name", src: "some-app-name_some_suffix_0.0.1_amd64.deb", expectedError: nil},
}

for i := range tests {
tt := tests[i]
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
fmt.Println(tt.appName, tt.src)
err := validateName(tt.appName, tt.src)
assert.Equal(t, tt.expectedError, err)
})
}
}
4 changes: 4 additions & 0 deletions publisher/publisher.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ func main() {
if err != nil {
l.Fatal(err)
}
// validate schemas
if err = config.ValidateSchemas(conf.AppName, uploadSchemas); err != nil {
l.Fatal(err)
}

if conf.LocalPackagesPath == "" {
d := download.NewDownloader(http.DefaultClient)
Expand Down
11 changes: 3 additions & 8 deletions publisher/upload/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,6 @@ import (
)

const (
//FileTypes
typeFile = "file"
typeZypp = "zypp"
typeYum = "yum"
typeApt = "apt"
repodataRpmPath = "/repodata/repomd.xml"
signatureRpmPath = "/repodata/repomd.xml.asc"
aptPoolMain = "pool/main/"
Expand All @@ -33,7 +28,7 @@ const (
)

func uploadArtifact(conf config.Config, schema config.UploadArtifactSchema, upload config.Upload) (err error) {
if upload.Type == typeFile {
if upload.Type == config.TypeFile {
utils.Logger.Println("Uploading file artifact")
for _, arch := range schema.Arch {
if len(upload.OsVersion) == 0 {
Expand All @@ -50,15 +45,15 @@ func uploadArtifact(conf config.Config, schema config.UploadArtifactSchema, uplo
}
}
}
} else if upload.Type == typeYum || upload.Type == typeZypp {
} else if upload.Type == config.TypeYum || upload.Type == config.TypeZypp {
utils.Logger.Println("Uploading rpm as yum or zypp")
for _, arch := range schema.Arch {
err = uploadRpm(conf, schema.Src, upload, arch)
if err != nil {
return err
}
}
} else if upload.Type == typeApt {
} else if upload.Type == config.TypeApt {
utils.Logger.Println("Uploading apt")
err = uploadApt(conf, schema.Src, upload, schema.Arch)
if err != nil {
Expand Down

0 comments on commit c8c99d6

Please sign in to comment.