diff --git a/cmd/exec/main.go b/cmd/exec/main.go index af170a9cb6..5a6099e5b9 100644 --- a/cmd/exec/main.go +++ b/cmd/exec/main.go @@ -18,7 +18,7 @@ import ( "github.com/spf13/viper" ) -const DefaultConfigName = ".direktiv.conf" +const DefaultConfigName = ".direktiv.yaml" // Flags var ( @@ -44,7 +44,7 @@ var ( localAbsPath string urlPrefix string urlWorkflow string - urlUpdateWorkflow string + // urlUpdateWorkflow string ) func main() { @@ -161,7 +161,6 @@ func cmdPrepareWorkflow(wfPath string) { urlPrefix = fmt.Sprintf("%s/api/namespaces/%s", addr, namespace) urlWorkflow = fmt.Sprintf("%s/tree/%s", urlPrefix, strings.TrimPrefix(path, "/")) - urlUpdateWorkflow = fmt.Sprintf("%s?op=update-workflow", urlWorkflow) } var rootCmd = &cobra.Command{ @@ -207,7 +206,7 @@ Will update the helloworld workflow and set the remote workflow variable 'data.j if err != nil { return err } - if strings.HasSuffix(localPath, ".yaml") { + if (strings.HasSuffix(localPath, ".yaml") || strings.HasSuffix(localPath, ".yml")) && !(strings.Contains(localPath, ".yaml.") || strings.Contains(localPath, ".yml.")) { pathsToUpdate = append(pathsToUpdate, localPath) } return nil @@ -222,12 +221,10 @@ Will update the helloworld workflow and set the remote workflow variable 'data.j cmd.PrintErrf("Found %v Local Workflow/s to update\n", len(pathsToUpdate)) for i, localPath := range pathsToUpdate { - path = filepath.ToSlash(strings.TrimSuffix(strings.TrimPrefix(localPath, filepath.Dir(configPath)), ".yaml")) - urlWorkflow = fmt.Sprintf("%s/tree/%s", urlPrefix, strings.TrimPrefix(path, "/")) - urlUpdateWorkflow = fmt.Sprintf("%s?op=update-workflow", urlWorkflow) + path = filepath.ToSlash(strings.TrimSuffix(strings.TrimSuffix(strings.TrimPrefix(localPath, filepath.Dir(configPath)), ".yaml"), ".yml")) cmd.PrintErrf("[%v/%v] Updating Namespace: '%s' Workflow: '%s'\n", i+1, len(pathsToUpdate), namespace, path) - err = updateRemoteWorkflow(urlUpdateWorkflow, localPath) + err = updateRemoteWorkflow(path, localPath) if err != nil { log.Fatalf("Failed to update remote workflow: %v\n", err) } @@ -278,7 +275,7 @@ Will update the helloworld workflow and set the remote workflow variable 'data.j instanceStatus := "pending" cmd.PrintErrf("Updating Namespace: '%s' Workflow: '%s'\n", namespace, path) - err := updateRemoteWorkflow(urlUpdateWorkflow, localAbsPath) + err := updateRemoteWorkflow(path, localAbsPath) if err != nil { log.Fatalf("Failed to update remote workflow: %v\n", err) } diff --git a/cmd/exec/operations.go b/cmd/exec/operations.go index b9dcc62765..84c7b1190a 100644 --- a/cmd/exec/operations.go +++ b/cmd/exec/operations.go @@ -9,6 +9,8 @@ import ( "net/http" "path/filepath" "strings" + + "github.com/direktiv/direktiv/pkg/util" ) func addAuthHeaders(req *http.Request) { @@ -72,16 +74,217 @@ func getLocalWorkflowVariables(absPath string) ([]string, error) { return varFiles, nil } -func updateRemoteWorkflow(url string, localPath string) error { - wfData, err := safeLoadFile(localPath) +func recurseMkdirParent(path string) error { + + dir, _ := filepath.Split(path) + if dir == "" || dir == "/" { + return nil + } + + dir = strings.TrimSuffix(dir, "/") + + err := recurseMkdirParent(dir) + if err != nil { + return err + } + + urlDir := fmt.Sprintf("%s/tree/%s", urlPrefix, strings.Trim(dir, "/")) + urlMkdir := fmt.Sprintf("%s?op=create-directory", urlDir) + + req, err := http.NewRequest( + http.MethodPut, + urlMkdir, + nil, + ) + if err != nil { + return fmt.Errorf("failed to create request file: %v", err) + } + + addAuthHeaders(req) + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return fmt.Errorf("failed to send request: %v", err) + } + + if resp.StatusCode != 200 && resp.StatusCode != http.StatusConflict { + errBody, err := ioutil.ReadAll(resp.Body) + if err == nil { + return fmt.Errorf("failed to create parent, server responsed with %s\n------DUMPING ERROR BODY ------\n%s", resp.Status, string(errBody)) + } + + return fmt.Errorf("failed to create parent, server responsed with %s\n------DUMPING ERROR BODY ------\nCould read response body", resp.Status) + } + + return err + +} + +func setWritable(path string) error { + + dir, _ := filepath.Split(path) + dir = strings.TrimSuffix(dir, "/") + + urlWorkflow = fmt.Sprintf("%s/tree/%s", urlPrefix, strings.TrimPrefix(path, "/")) + urlGetNode := fmt.Sprintf("%s", urlWorkflow) + + req, err := http.NewRequest( + http.MethodGet, + urlGetNode, + nil, + ) + if err != nil { + return fmt.Errorf("failed to create request file: %v", err) + } + + addAuthHeaders(req) + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return fmt.Errorf("failed to send request: %v", err) + } + + if resp.StatusCode != 200 { + if resp.StatusCode == http.StatusNotFound { + err = setWritable(dir) + if err != nil { + return err + } + return nil + } + + errBody, err := ioutil.ReadAll(resp.Body) + if err == nil { + return fmt.Errorf("failed to get node information, server responsed with %s\n------DUMPING ERROR BODY ------\n%s", resp.Status, string(errBody)) + } + + return fmt.Errorf("failed to get node information, server responsed with %s\n------DUMPING ERROR BODY ------\nCould read response body", resp.Status) + } + + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("failed to read response: %v", err) + } + + m := make(map[string]interface{}) + err = json.Unmarshal(data, &m) + if err != nil { + return fmt.Errorf("failed to unmarshal response: %v", err) + } + + x, exists := m["node"] + if !exists { + return fmt.Errorf("unexpected response: %v", string(data)) + } + + m2, ok := x.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected response: %v", string(data)) + } + + x, exists = m2["readOnly"] + if !exists { + return fmt.Errorf("unexpected response: %v", string(data)) + } + + ro, ok := x.(bool) + if !ok { + return fmt.Errorf("unexpected response: %v", string(data)) + } + + if ro == false { + return nil + } + + x, exists = m2["expandedType"] + if !exists { + return fmt.Errorf("unexpected response: %v", string(data)) + } + + et, ok := x.(string) + if !ok { + return fmt.Errorf("unexpected response: %v", string(data)) + } + + switch et { + case util.InodeTypeGit: + + urlLockMirror := fmt.Sprintf("%s?op=lock-mirror", urlWorkflow) + + req, err := http.NewRequest( + http.MethodPost, + urlLockMirror, + nil, + ) + if err != nil { + return fmt.Errorf("failed to create request file: %v", err) + } + + addAuthHeaders(req) + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return fmt.Errorf("failed to send request: %v", err) + } + + if resp.StatusCode != 200 { + errBody, err := ioutil.ReadAll(resp.Body) + if err == nil { + return fmt.Errorf("failed to get node information, server responsed with %s\n------DUMPING ERROR BODY ------\n%s", resp.Status, string(errBody)) + } + + return fmt.Errorf("failed to get node information, server responsed with %s\n------DUMPING ERROR BODY ------\nCould read response body", resp.Status) + } + + default: + + err = setWritable(dir) + if err != nil { + return err + } + + } + + return nil + +} + +func updateRemoteWorkflow(path string, localPath string) error { + + err := setWritable(path) + if err != nil { + log.Fatalf("Failed to make writable: %v", err) + } + + err = recurseMkdirParent(path) + if err != nil { + log.Fatalf("Failed to create parent directory: %v", err) + } + + urlWorkflow = fmt.Sprintf("%s/tree/%s", urlPrefix, strings.TrimPrefix(path, "/")) + + urlUpdate := fmt.Sprintf("%s?op=update-workflow", urlWorkflow) + urlCreate := fmt.Sprintf("%s?op=create-workflow", urlWorkflow) + + buf, err := safeLoadFile(localPath) if err != nil { log.Fatalf("Failed to load workflow file: %v", err) } + data, err := ioutil.ReadAll(buf) + if err != nil { + log.Fatalf("Failed to load workflow file: %v", err) + } + + updateFailed := false + url := urlUpdate + method := http.MethodPost + +retry: req, err := http.NewRequest( - http.MethodPost, + method, url, - wfData, + bytes.NewReader(data), ) if err != nil { return fmt.Errorf("failed to create request file: %v", err) @@ -95,6 +298,12 @@ func updateRemoteWorkflow(url string, localPath string) error { } if resp.StatusCode != 200 { + if resp.StatusCode == http.StatusNotFound && !updateFailed { + updateFailed = true + url = urlCreate + method = http.MethodPut + goto retry + } errBody, err := ioutil.ReadAll(resp.Body) if err == nil { return fmt.Errorf("failed to update workflow, server responsed with %s\n------DUMPING ERROR BODY ------\n%s", resp.Status, string(errBody)) diff --git a/pkg/flow/grpc-nodes.go b/pkg/flow/grpc-nodes.go index 5c4b4f470e..077d0bba36 100644 --- a/pkg/flow/grpc-nodes.go +++ b/pkg/flow/grpc-nodes.go @@ -437,6 +437,9 @@ func (flow *flow) CreateDirectory(ctx context.Context, req *grpc.CreateDirectory pino: pino.ino, path: path, }) + if err != nil { + return nil, err + } err = tx.Commit() if err != nil {