Skip to content

Commit

Permalink
Parse Manifest and Default CR
Browse files Browse the repository at this point in the history
  • Loading branch information
nesmabadr committed Sep 9, 2024
1 parent f7a3e86 commit edfaf2b
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 4 deletions.
4 changes: 3 additions & 1 deletion cmd/modulectl/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,10 @@ func NewCmd() (*cobra.Command, error) {

func buildModuleService() (*create.Service, error) {
fileSystemUtil := &filesystem.Util{}
tmpFileSystem := filesystem.NewTempFileSystem()
defer tmpFileSystem.RemoveTempFiles()

moduleConfigService, err := moduleconfigreader.NewService(fileSystemUtil)
moduleConfigService, err := moduleconfigreader.NewService(fileSystemUtil, tmpFileSystem)
if err != nil {
return nil, fmt.Errorf("failed to create module config service: %w", err)
}
Expand Down
12 changes: 12 additions & 0 deletions internal/service/create/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
type ModuleConfigService interface {
ParseModuleConfig(configFilePath string) (*contentprovider.ModuleConfig, error)
ValidateModuleConfig(moduleConfig *contentprovider.ModuleConfig) error
GetDefaultCRPath(defaultCRPath string) (string, error)
GetManifestPath(manifestPath string) (string, error)
}

type Service struct {
Expand Down Expand Up @@ -40,5 +42,15 @@ func (s *Service) CreateModule(opts Options) error {
return fmt.Errorf("%w: failed to value module config", err)
}

moduleConfig.DefaultCRPath, err = s.moduleConfigService.GetDefaultCRPath(moduleConfig.DefaultCRPath)
if err != nil {
return fmt.Errorf("%w: failed to get default CR path", err)
}

moduleConfig.ManifestPath, err = s.moduleConfigService.GetManifestPath(moduleConfig.ManifestPath)
if err != nil {
return fmt.Errorf("%w: failed to get manifest path", err)
}

return nil
}
63 changes: 60 additions & 3 deletions internal/service/moduleconfig/reader/moduleconfig_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package moduleconfigreader

import (
"fmt"
"net/url"

"gopkg.in/yaml.v3"

Expand All @@ -10,21 +11,37 @@ import (
"github.com/kyma-project/modulectl/internal/service/contentprovider"
)

const (
defaultCRFilePattern = "kyma-module-default-cr-*.yaml"
defaultManifestFilePattern = "kyma-module-manifest-*.yaml"
)

type FileSystem interface {
ReadFile(path string) ([]byte, error)
}

type TempFileSystem interface {
DownloadTempFile(dir, pattern string, url *url.URL) (string, error)
RemoveTempFiles() []error
}

type Service struct {
fileSystem FileSystem
fileSystem FileSystem
tmpFileSystem TempFileSystem
}

func NewService(fileSystem FileSystem) (*Service, error) {
func NewService(fileSystem FileSystem, tmpFileSystem TempFileSystem) (*Service, error) {
if fileSystem == nil {
return nil, fmt.Errorf("%w: fileSystem must not be nil", commonerrors.ErrInvalidArg)
}

if tmpFileSystem == nil {
return nil, fmt.Errorf("%w: tmpFileSystem must not be nil", commonerrors.ErrInvalidArg)
}

return &Service{
fileSystem: fileSystem,
fileSystem: fileSystem,
tmpFileSystem: tmpFileSystem,
}, nil
}

Expand All @@ -42,6 +59,42 @@ func (s *Service) ParseModuleConfig(configFilePath string) (*contentprovider.Mod
return moduleConfig, nil
}

func (s *Service) GetDefaultCRPath(defaultCRPath string) (string, error) {
if defaultCRPath == "" {
return defaultCRPath, nil
}

path := defaultCRPath
if parsedURL, err := s.ParseURL(defaultCRPath); err == nil {
path, err = s.tmpFileSystem.DownloadTempFile("", defaultCRFilePattern, parsedURL)
if err != nil {
return "", fmt.Errorf("failed to download default CR file: %w", err)
}
}

return path, nil
}

func (s *Service) GetManifestPath(manifestPath string) (string, error) {
path := manifestPath
if parsedURL, err := s.ParseURL(manifestPath); err == nil {
path, err = s.tmpFileSystem.DownloadTempFile("", defaultManifestFilePattern, parsedURL)
if err != nil {
return "", fmt.Errorf("failed to download default CR file: %w", err)
}
}

return path, nil
}

func (s *Service) ParseURL(urlString string) (*url.URL, error) {
urlParsed, err := url.Parse(urlString)
if err != nil && urlParsed.Scheme != "" && urlParsed.Host != "" {
return urlParsed, nil
}
return nil, fmt.Errorf("%w: parsing url failed for %s", commonerrors.ErrInvalidArg, urlString)
}

func (*Service) ValidateModuleConfig(moduleConfig *contentprovider.ModuleConfig) error {
if err := validations.ValidateModuleName(moduleConfig.Name); err != nil {
return fmt.Errorf("failed to validate module name: %w", err)
Expand All @@ -59,5 +112,9 @@ func (*Service) ValidateModuleConfig(moduleConfig *contentprovider.ModuleConfig)
return fmt.Errorf("failed to validate module namespace: %w", err)
}

if moduleConfig.ManifestPath == "" {
return fmt.Errorf("%w: manifest path must not be empty", commonerrors.ErrInvalidArg)
}

return nil
}
13 changes: 13 additions & 0 deletions internal/service/moduleconfig/reader/moduleconfig_reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package moduleconfigreader_test

import (
"errors"
"net/url"
"testing"

"github.com/stretchr/testify/assert"
Expand All @@ -19,6 +20,7 @@ const (
func Test_ParseModuleConfig_ReturnsError_WhenFileReaderReturnsError(t *testing.T) {
svc, _ := moduleconfigreader.NewService(
&fileDoesNotExistStub{},
&tmpfileSystemStub{},
)

result, err := svc.ParseModuleConfig(moduleConfigFile)
Expand All @@ -30,6 +32,7 @@ func Test_ParseModuleConfig_ReturnsError_WhenFileReaderReturnsError(t *testing.T
func Test_ParseModuleConfig_ReturnsCorrect_ModuleConfig(t *testing.T) {
svc, _ := moduleconfigreader.NewService(
&fileExistsStub{},
&tmpfileSystemStub{},
)

result, err := svc.ParseModuleConfig(moduleConfigFile)
Expand Down Expand Up @@ -78,6 +81,16 @@ func (*fileExistsStub) ReadFile(_ string) ([]byte, error) {
return yaml.Marshal(moduleConfig)
}

type tmpfileSystemStub struct{}

func (*tmpfileSystemStub) DownloadTempFile(_ string, _ string, _ *url.URL) (string, error) {
return "test", nil
}

func (*tmpfileSystemStub) RemoveTempFiles() []error {
return nil
}

type fileDoesNotExistStub struct{}

func (*fileDoesNotExistStub) FileExists(_ string) (bool, error) {
Expand Down
76 changes: 76 additions & 0 deletions tools/filesystem/tempfilesystem.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package filesystem

import (
"context"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"os"
"time"
)

const httpGetTimeout = 20 * time.Second

var errBadHTTPStatus = errors.New("bad http status")

type TempFileSystem struct {
files []*os.File
}

func NewTempFileSystem() *TempFileSystem {
return &TempFileSystem{files: []*os.File{}}
}

func (fs *TempFileSystem) DownloadTempFile(dir, pattern string, url *url.URL) (string, error) {
bytes, err := getBytesFromURL(url)
if err != nil {
return "", fmt.Errorf("failed to download file from %s: %w", url, err)
}

tmpFile, err := os.CreateTemp(dir, pattern)
if err != nil {
return "", fmt.Errorf("failed to create temp file with pattern %s: %w", pattern, err)
}
defer tmpFile.Close()
fs.files = append(fs.files, tmpFile)
if _, err := tmpFile.Write(bytes); err != nil {
return "", fmt.Errorf("failed to write to temp file %s: %w", tmpFile.Name(), err)
}
return tmpFile.Name(), nil
}

func (fs *TempFileSystem) RemoveTempFiles() []error {
var errs []error
for _, file := range fs.files {
err := os.Remove(file.Name())
if err != nil {
errs = append(errs, err)
}
}
fs.files = []*os.File{}
return errs
}

func getBytesFromURL(url *url.URL) ([]byte, error) {
ctx, cancel := context.WithTimeout(context.Background(), httpGetTimeout)
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url.String(), nil)
if err != nil {
return nil, fmt.Errorf("http GET request failed for %s: %w", url, err)
}
defer req.Body.Close()

if req.Response.StatusCode != http.StatusOK {
return nil, fmt.Errorf("%w: bad status for GET request to %s: %q", errBadHTTPStatus, url,
req.Response.StatusCode)
}

data, err := io.ReadAll(req.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body from %s: %w", url, err)
}

return data, nil
}

0 comments on commit edfaf2b

Please sign in to comment.