Skip to content

Commit

Permalink
fix
Browse files Browse the repository at this point in the history
Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
  • Loading branch information
eddycharly committed Apr 26, 2024
1 parent d905c9e commit 0bd7a50
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 183 deletions.
18 changes: 15 additions & 3 deletions pkg/cmd/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"io/fs"
"net/http"
"os"

Expand Down Expand Up @@ -164,16 +165,27 @@ func errorToStatus(err error) metav1.Status {
}

func (c *commandFlags) Run(cmd *cobra.Command, args []string) error {
var schemaPatchesFs, localSchemasFs fs.FS
if c.schemaPatchesDir != "" {
schemaPatchesFs = os.DirFS(c.schemaPatchesDir)
}
if c.localSchemasDir != "" {
localSchemasFs = os.DirFS(c.localSchemasDir)
}
var localCRDsFileSystems []fs.FS
for _, current := range c.localCRDsDir {
localCRDsFileSystems = append(localCRDsFileSystems, os.DirFS(current))
}
// tool fetches openapi in the following priority order:
factory, err := validator.New(
openapiclient.NewOverlay(
// apply user defined patches on top of the final schema
openapiclient.PatchLoaderFromDirectory(nil, c.schemaPatchesDir),
openapiclient.PatchLoaderFromDirectory(schemaPatchesFs),
openapiclient.NewComposite(
// consult local OpenAPI
openapiclient.NewLocalSchemaFiles(nil, c.localSchemasDir),
openapiclient.NewLocalSchemaFiles(localSchemasFs),
// consult local CRDs
openapiclient.NewLocalCRDFiles(nil, c.localCRDsDir),
openapiclient.NewLocalCRDFiles(localCRDsFileSystems...),
openapiclient.NewOverlay(
// Hand-written hardcoded patches.
openapiclient.HardcodedPatchLoader(c.version),
Expand Down
26 changes: 12 additions & 14 deletions pkg/openapiclient/local_crds.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"encoding/json"
"fmt"
"io/fs"
"path"

"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
"k8s.io/apiextensions-apiserver/pkg/apiserver"
Expand Down Expand Up @@ -33,44 +32,43 @@ var metadataSchemas map[string]*spec.Schema = func() map[string]*spec.Schema {

// client which provides openapi read from files on disk
type localCRDsClient struct {
fs fs.FS
dirs []string
fileSystems []fs.FS
}

// Dir should have openapi files following directory layout:
// myCRD.yaml (groupversions read from file)
func NewLocalCRDFiles(fs fs.FS, dirPaths []string) openapi.Client {
func NewLocalCRDFiles(fs ...fs.FS) openapi.Client {
return &localCRDsClient{
fs: fs,
dirs: dirPaths,
fileSystems: fs,
}
}

func (k *localCRDsClient) Paths() (map[string]openapi.GroupVersion, error) {
if len(k.dirs) == 0 {
if len(k.fileSystems) == 0 {
return nil, nil
}
var documents []utils.Document

for _, dir := range k.dirs {
files, err := fs.ReadDir(k.fs, dir)
for _, current := range k.fileSystems {
files, err := fs.ReadDir(current, ".")
if err != nil {
return nil, fmt.Errorf("error listing %s: %w", dir, err)
if crossPlatformCheckDirExists(current, ".") {
return nil, fmt.Errorf("error listing: %w", err)
}
}

for _, f := range files {
path := path.Join(dir, f.Name())
path := f.Name()
if f.IsDir() {
continue
}
if utils.IsJson(f.Name()) {
fileBytes, err := fs.ReadFile(k.fs, path)
fileBytes, err := fs.ReadFile(current, path)
if err != nil {
return nil, fmt.Errorf("failed to read %s: %w", path, err)
}
documents = append(documents, fileBytes)
} else if utils.IsYaml(f.Name()) {
fileBytes, err := fs.ReadFile(k.fs, path)
fileBytes, err := fs.ReadFile(current, path)
if err != nil {
return nil, fmt.Errorf("failed to read %s: %w", path, err)
}
Expand Down
96 changes: 27 additions & 69 deletions pkg/openapiclient/local_crds_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,60 +14,44 @@ import (

func TestNewLocalCRDFiles(t *testing.T) {
tests := []struct {
name string
fs fs.FS
dirPaths []string
want openapi.Client
name string
fileSystems []fs.FS
want openapi.Client
}{{
name: "fs nil and dir empty",
name: "no fs",
want: &localCRDsClient{},
}, {
name: "only dir",
dirPaths: []string{"test"},
name: "one fs",
fileSystems: []fs.FS{os.DirFS("test")},
want: &localCRDsClient{
dirs: []string{"test"},
fileSystems: []fs.FS{os.DirFS("test")},
},
}, {
name: "multiple dirs",
dirPaths: []string{"test", "test2"},
name: "multiple dirs",
fileSystems: []fs.FS{os.DirFS("test"), os.DirFS("test2")},
want: &localCRDsClient{
dirs: []string{"test", "test2"},
},
}, {
name: "only fs",
fs: os.DirFS("."),
want: &localCRDsClient{
fs: os.DirFS("."),
},
}, {
name: "both fs and dir",
fs: os.DirFS("."),
dirPaths: []string{"test"},
want: &localCRDsClient{
fs: os.DirFS("."),
dirs: []string{"test"},
fileSystems: []fs.FS{os.DirFS("test"), os.DirFS("test2")},
},
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := NewLocalCRDFiles(tt.fs, tt.dirPaths)
got := NewLocalCRDFiles(tt.fileSystems...)
require.Equal(t, tt.want, got, "NewLocalCRDFiles not equal")
})
}
}

func Test_localCRDsClient_Paths(t *testing.T) {
tests := []struct {
name string
fs fs.FS
dirs []string
want map[string]sets.Set[string]
wantErr bool
name string
fileSystems []fs.FS
want map[string]sets.Set[string]
wantErr bool
}{{
name: "fs nil and dir empty",
name: "no fs",
}, {
name: "only dir",
dirs: []string{"../../testcases/crds"},
name: "one fs",
fileSystems: []fs.FS{os.DirFS("../../testcases/crds")},
want: map[string]sets.Set[string]{
"apis/batch.x-k8s.io/v1alpha1": sets.New(
"batch.x-k8s.io/v1alpha1.JobSet",
Expand All @@ -87,8 +71,8 @@ func Test_localCRDsClient_Paths(t *testing.T) {
),
},
}, {
name: "multiple dirs",
dirs: []string{"../../testcases/crds", "../../testcases/more-crds"},
name: "two fs",
fileSystems: []fs.FS{os.DirFS("../../testcases/crds"), os.DirFS("../../testcases/more-crds")},
want: map[string]sets.Set[string]{
"apis/batch.x-k8s.io/v1alpha1": sets.New(
"batch.x-k8s.io/v1alpha1.JobSet",
Expand All @@ -111,43 +95,17 @@ func Test_localCRDsClient_Paths(t *testing.T) {
),
},
}, {
name: "only fs",
fs: os.DirFS("../../testcases/crds"),
}, {
name: "both fs and dir",
fs: os.DirFS("../../testcases"),
dirs: []string{"crds"},
want: map[string]sets.Set[string]{
"apis/batch.x-k8s.io/v1alpha1": sets.New(
"batch.x-k8s.io/v1alpha1.JobSet",
),
"apis/stable.example.com/v1": sets.New(
"stable.example.com/v1.CELBasic",
),
"apis/acme.cert-manager.io/v1": sets.New(
"acme.cert-manager.io/v1.Challenge",
"acme.cert-manager.io/v1.Order",
),
"apis/cert-manager.io/v1": sets.New(
"cert-manager.io/v1.Certificate",
"cert-manager.io/v1.CertificateRequest",
"cert-manager.io/v1.ClusterIssuer",
"cert-manager.io/v1.Issuer",
),
},
}, {
name: "invalid dir",
dirs: []string{"invalid"},
wantErr: true,
name: "does not exist",
fileSystems: []fs.FS{os.DirFS("../../invalid")},
want: map[string]sets.Set[string]{},
}, {
name: "invalid fs",
fs: os.DirFS("../../invalid"),
dirs: []string{"."},
wantErr: true,
name: "not a directory",
fileSystems: []fs.FS{os.DirFS("../../testcases/schemas/error_not_a_dir")},
wantErr: true,
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
k := NewLocalCRDFiles(tt.fs, tt.dirs)
k := NewLocalCRDFiles(tt.fileSystems...)
paths, err := k.Paths()
if (err != nil) != tt.wantErr {
t.Errorf("localCRDsClient.Paths() error = %v, wantErr %v", err, tt.wantErr)
Expand Down
46 changes: 31 additions & 15 deletions pkg/openapiclient/local_schemas.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,34 +14,40 @@ import (

// client which provides openapi read from files on disk
type localSchemasClient struct {
fs fs.FS
dir string
fs fs.FS
}

// Dir should have openapi files following directory layout:
// /<apis>/<group>/<version>.json
// /api/<version>.json
func NewLocalSchemaFiles(fs fs.FS, dirPath string) openapi.Client {
func NewLocalSchemaFiles(fs fs.FS) openapi.Client {
return &localSchemasClient{
fs: fs,
dir: dirPath,
fs: fs,
}
}

func (k *localSchemasClient) Paths() (map[string]openapi.GroupVersion, error) {
if len(k.dir) == 0 {
if k.fs == nil {
return nil, nil
}
// check if '.' can be listed
if _, err := fs.ReadDir(k.fs, "."); err != nil {
if crossPlatformCheckDirExists(k.fs, ".") {
return nil, fmt.Errorf("error listing %s: %w", ".", err)
}
}
res := map[string]openapi.GroupVersion{}
apiGroups, err := fs.ReadDir(k.fs, path.Join(k.dir, "apis"))
if err != nil && !errors.Is(err, fs.ErrNotExist) {
return nil, fmt.Errorf("failed reading local files dir %s: %w", path.Join(k.dir, "apis"), err)
apiGroups, err := fs.ReadDir(k.fs, "apis")
if err != nil {
if crossPlatformCheckDirExists(k.fs, "apis") {
return nil, fmt.Errorf("error listing %s: %w", "apis", err)
}
}
for _, f := range apiGroups {
groupPath := path.Join(k.dir, "apis", f.Name())
groupPath := path.Join("apis", f.Name())
versions, err := fs.ReadDir(k.fs, groupPath)
if err != nil {
return nil, fmt.Errorf("failed reading local files dir %s: %w", groupPath, err)
return nil, fmt.Errorf("error listing %s: %w", groupPath, err)
}
for _, v := range versions {
if !utils.IsJson(v.Name()) {
Expand All @@ -52,17 +58,27 @@ func (k *localSchemasClient) Paths() (map[string]openapi.GroupVersion, error) {
res[apisPath] = groupversion.NewForFile(k.fs, path.Join(groupPath, v.Name()))
}
}
coregroup, err := fs.ReadDir(k.fs, path.Join(k.dir, "api"))
if err != nil && !errors.Is(err, fs.ErrNotExist) {
return nil, fmt.Errorf("failed reading local files dir %s: %w", path.Join(k.dir, "api"), err)
coregroup, err := fs.ReadDir(k.fs, "api")
if err != nil {
if crossPlatformCheckDirExists(k.fs, "api") {
return nil, fmt.Errorf("error listing %s: %w", "api", err)
}
}
for _, v := range coregroup {
if !utils.IsJson(v.Name()) {
continue
}
name := strings.TrimSuffix(v.Name(), path.Ext(v.Name()))
apiPath := path.Join("api", name)
res[apiPath] = groupversion.NewForFile(k.fs, path.Join(k.dir, "api", v.Name()))
res[apiPath] = groupversion.NewForFile(k.fs, path.Join("api", v.Name()))
}
return res, nil
}

func crossPlatformCheckDirExists(f fs.FS, path string) bool {
_, err := fs.Stat(f, path)
if err != nil {
return !errors.Is(err, fs.ErrNotExist)
}
return true
}

0 comments on commit 0bd7a50

Please sign in to comment.