Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(cli): update apply command with new resourcemanager client (#2841)
- Loading branch information
Showing
44 changed files
with
414 additions
and
166 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
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,99 @@ | ||
package fileutil | ||
|
||
import ( | ||
"bytes" | ||
"errors" | ||
"fmt" | ||
"io" | ||
"os" | ||
"path/filepath" | ||
"regexp" | ||
"strings" | ||
) | ||
|
||
type file struct { | ||
path string | ||
contents []byte | ||
} | ||
|
||
func Read(filePath string) (file, error) { | ||
b, err := os.ReadFile(filePath) | ||
if err != nil { | ||
return file{}, fmt.Errorf("could not read definition file %s: %w", filePath, err) | ||
} | ||
|
||
return New(filePath, b), nil | ||
} | ||
|
||
func New(path string, b []byte) file { | ||
file := file{ | ||
contents: b, | ||
path: path, | ||
} | ||
|
||
return file | ||
} | ||
|
||
func (f file) Reader() io.Reader { | ||
return bytes.NewReader(f.contents) | ||
} | ||
|
||
var ( | ||
hasIDRegex = regexp.MustCompile(`(?m:^\s+id:\s*)`) | ||
indentSizeRegex = regexp.MustCompile(`(?m:^(\s+)\w+)`) | ||
) | ||
|
||
var ErrFileHasID = errors.New("file already has ID") | ||
|
||
func (f file) HasID() bool { | ||
fileID := hasIDRegex.Find(f.contents) | ||
return fileID != nil | ||
} | ||
|
||
func (f file) SetID(id string) (file, error) { | ||
if f.HasID() { | ||
return f, ErrFileHasID | ||
} | ||
|
||
indent := indentSizeRegex.FindSubmatchIndex(f.contents) | ||
if len(indent) < 4 { | ||
return f, fmt.Errorf("cannot detect indentation size") | ||
} | ||
|
||
indentSize := indent[3] - indent[2] | ||
// indent[2] is the index of the first indentation. | ||
// we can assume that's the first line within the `specs` block | ||
// so we can use it as the place to inejct the ID | ||
|
||
var newContents []byte | ||
newContents = append(newContents, f.contents[0:indent[2]]...) | ||
|
||
newContents = append(newContents, []byte(strings.Repeat(" ", indentSize))...) | ||
newContents = append(newContents, []byte("id: "+id+"\n")...) | ||
|
||
newContents = append(newContents, f.contents[indent[2]:]...) | ||
|
||
return New(f.path, newContents), nil | ||
} | ||
|
||
func (f file) AbsDir() string { | ||
abs, err := filepath.Abs(f.path) | ||
if err != nil { | ||
panic(fmt.Errorf(`cannot get absolute path from "%s": %w`, f.path, err)) | ||
} | ||
|
||
return filepath.Dir(abs) | ||
} | ||
|
||
func (f file) Write() (file, error) { | ||
err := os.WriteFile(f.path, f.contents, 0644) | ||
if err != nil { | ||
return f, fmt.Errorf("could not write file %s: %w", f.path, err) | ||
} | ||
|
||
return Read(f.path) | ||
} | ||
|
||
func (f file) ReadAll() (string, error) { | ||
return string(f.contents), nil | ||
} |
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,30 @@ | ||
package fileutil_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/kubeshop/tracetest/cli/pkg/fileutil" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestSetID(t *testing.T) { | ||
t.Run("NoID", func(t *testing.T) { | ||
f, err := fileutil.Read("../../testdata/definitions/valid_http_test_definition.yml") | ||
require.NoError(t, err) | ||
|
||
f, err = f.SetID("new-id") | ||
require.NoError(t, err) | ||
|
||
assert.True(t, f.HasID()) | ||
}) | ||
|
||
t.Run("WithID", func(t *testing.T) { | ||
f, err := fileutil.Read("../../testdata/definitions/valid_http_test_definition_with_id.yml") | ||
require.NoError(t, err) | ||
|
||
_, err = f.SetID("new-id") | ||
require.ErrorIs(t, err, fileutil.ErrFileHasID) | ||
}) | ||
|
||
} |
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,99 @@ | ||
package resourcemanager | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"io" | ||
"net/http" | ||
|
||
"github.com/Jeffail/gabs/v2" | ||
"github.com/kubeshop/tracetest/cli/pkg/fileutil" | ||
) | ||
|
||
const VerbApply Verb = "apply" | ||
|
||
func (c client) Apply(ctx context.Context, filePath string, requestedFormat Format) (string, error) { | ||
inputFile, err := fileutil.Read(filePath) | ||
if err != nil { | ||
return "", fmt.Errorf("cannot read file %s: %w", filePath, err) | ||
} | ||
|
||
url := c.client.url(c.resourceNamePlural) | ||
req, err := http.NewRequestWithContext(ctx, http.MethodPut, url.String(), inputFile.Reader()) | ||
if err != nil { | ||
return "", fmt.Errorf("cannot build Apply request: %w", err) | ||
} | ||
|
||
// we want the response inthe user's requested format | ||
err = requestedFormat.BuildRequest(req, VerbApply) | ||
if err != nil { | ||
return "", fmt.Errorf("cannot build Apply request: %w", err) | ||
} | ||
|
||
// the files must be in yaml format, so we can safely force the content type, | ||
// even if it doesn't matcht he user's requested format | ||
yamlFormat, err := Formats.Get(FormatYAML) | ||
if err != nil { | ||
return "", fmt.Errorf("cannot get json format: %w", err) | ||
} | ||
req.Header.Set("Content-Type", yamlFormat.ContentType()) | ||
|
||
// final request looks like this: | ||
// PUT {server}/{resourceNamePlural} | ||
// Content-Type: text/yaml | ||
// Accept: {requestedFormat.contentType} | ||
// | ||
// {yamlFileContent} | ||
// | ||
// This means that we'll send the request body as YAML (read from the user provided file) | ||
// and we'll get the reponse in the users's requrested format. | ||
resp, err := c.client.do(req) | ||
if err != nil { | ||
return "", fmt.Errorf("cannot execute Apply request: %w", err) | ||
} | ||
defer resp.Body.Close() | ||
|
||
if !isSuccessResponse(resp) { | ||
err := parseRequestError(resp, requestedFormat) | ||
|
||
return "", fmt.Errorf("could not Apply resource: %w", err) | ||
} | ||
|
||
body, err := io.ReadAll(resp.Body) | ||
if err != nil { | ||
return "", fmt.Errorf("cannot read Apply response: %w", err) | ||
} | ||
|
||
// if the original file doesn't have an ID, we need to get the server generated ID from the response | ||
// and write it to the original file | ||
if !inputFile.HasID() { | ||
|
||
jsonBody, err := requestedFormat.ToJSON(body) | ||
if err != nil { | ||
return "", fmt.Errorf("cannot convert response body to JSON format: %w", err) | ||
} | ||
|
||
parsed, err := gabs.ParseJSON(jsonBody) | ||
if err != nil { | ||
return "", fmt.Errorf("cannot parse Apply response: %w", err) | ||
} | ||
|
||
id, ok := parsed.Path("spec.id").Data().(string) | ||
if !ok { | ||
return "", fmt.Errorf("cannot get ID from Apply response") | ||
} | ||
|
||
inputFile, err = inputFile.SetID(id) | ||
if err != nil { | ||
return "", fmt.Errorf("cannot set ID on input file: %w", err) | ||
} | ||
|
||
_, err = inputFile.Write() | ||
if err != nil { | ||
return "", fmt.Errorf("cannot write updated input file: %w", err) | ||
} | ||
|
||
} | ||
|
||
return requestedFormat.Format(string(body), c.tableConfig) | ||
} |
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.