Skip to content

Commit 1f3d13c

Browse files
feat: expose the m2k graph file using the API (#110)
Signed-off-by: Harikrishnan Balagopal <[email protected]>
1 parent caf66db commit 1f3d13c

File tree

4 files changed

+106
-0
lines changed

4 files changed

+106
-0
lines changed

internal/filesystem/filesystem.go

+62
Original file line numberDiff line numberDiff line change
@@ -1846,6 +1846,68 @@ func (fs *FileSystem) readProjectOutput(t *bolt.Tx, workspaceId, projectId, proj
18461846
return projOutput, f, nil
18471847
}
18481848

1849+
// ReadProjectOutputGraph returns the graph file from the target artifacts for an application
1850+
func (fs *FileSystem) ReadProjectOutputGraph(workspaceId, projectId, projOutputId string) (projOutput types.ProjectOutput, file io.Reader, err error) {
1851+
db, err := fs.GetDatabase(true)
1852+
if err != nil {
1853+
return projOutput, file, err
1854+
}
1855+
defer db.Close()
1856+
err = db.View(func(t *bolt.Tx) error {
1857+
projOutput, file, err = fs.readProjectOutputGraph(t, workspaceId, projectId, projOutputId)
1858+
return err
1859+
})
1860+
return projOutput, file, err
1861+
}
1862+
1863+
func (fs *FileSystem) readProjectOutputGraph(t *bolt.Tx, workspaceId, projectId, projOutputId string) (projOutput types.ProjectOutput, file io.Reader, err error) {
1864+
project, err := fs.readProject(t, workspaceId, projectId)
1865+
if err != nil {
1866+
return types.ProjectOutput{}, nil, err
1867+
}
1868+
projOutput, ok := project.Outputs[projOutputId]
1869+
if !ok {
1870+
return projOutput, nil, types.ErrorDoesNotExist{Id: projOutputId}
1871+
}
1872+
if projOutput.Status == types.ProjectOutputStatusInProgress {
1873+
return projOutput, nil, types.ErrorOngoing{Id: projOutputId}
1874+
}
1875+
if projOutput.Status != types.ProjectOutputStatusDoneSuccess {
1876+
if projOutput.Status == types.ProjectOutputStatusDoneError {
1877+
return projOutput, nil, types.ErrorValidation{Reason: fmt.Sprintf("an error occurred during transformation of the output %s of project %s", projOutputId, projectId)}
1878+
}
1879+
return projOutput, nil, types.ErrorDoesNotExist{Id: projOutputId}
1880+
}
1881+
curDir := filepath.Join(common.Config.DataDir, PROJECTS_DIR, projectId, PROJECT_OUTPUTS_DIR, projOutputId, "output")
1882+
if err := fs.processGraph(curDir); err != nil {
1883+
return projOutput, nil, fmt.Errorf("failed to process the project output graph file inside the output directory %s . Error: %q", curDir, err)
1884+
}
1885+
projOutputPath := filepath.Join(curDir, "m2k-proc-graph.json")
1886+
f, err := os.Open(projOutputPath)
1887+
if err != nil {
1888+
return projOutput, nil, fmt.Errorf("failed to read the project output file at path %s . Error: %q", projOutputPath, err)
1889+
}
1890+
return projOutput, f, nil
1891+
}
1892+
1893+
func (fs *FileSystem) processGraph(currentRunDir string) error {
1894+
logrus.Infof("Starting graph at directory %s", currentRunDir)
1895+
cmdArgs := []string{"graph", "--output", "m2k-proc-graph.json"}
1896+
logrus.Infof("graph cmdArgs: %+v", cmdArgs)
1897+
ctx := context.Background()
1898+
if common.Config.PlanTimeoutSeconds > 0 {
1899+
var cancel context.CancelFunc
1900+
ctx, cancel = context.WithTimeout(ctx, time.Duration(common.Config.PlanTimeoutSeconds)*time.Second)
1901+
defer cancel()
1902+
}
1903+
cmd := exec.CommandContext(ctx, common.APP_NAME, cmdArgs...)
1904+
cmd.Dir = currentRunDir
1905+
if err := cmd.Run(); err != nil {
1906+
return fmt.Errorf("failed to start the graph command. Error: %q", err)
1907+
}
1908+
return nil
1909+
}
1910+
18491911
// DeleteProjectOutput deletes the project output
18501912
func (fs *FileSystem) DeleteProjectOutput(workspaceId, projectId, projOutputId string) error {
18511913
db, err := fs.GetDatabase(false)

internal/filesystem/interface.go

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ type IFileSystem interface {
4747
StartTransformation(workspaceId, projectId string, projOutput types.ProjectOutput, plan io.Reader, debugMode bool) error
4848
ResumeTransformation(workspaceId, projectId, projOutputId string, debugMode bool) error
4949
ReadProjectOutput(workspaceId, projectId, projOutputId string) (projOutput types.ProjectOutput, file io.Reader, err error)
50+
ReadProjectOutputGraph(workspaceId, projectId, projOutputId string) (projOutput types.ProjectOutput, file io.Reader, err error)
5051
DeleteProjectOutput(workspaceId, projectId, projOutputId string) error
5152
GetQuestion(workspaceId, projectId, projOutputId string) (problem string, err error)
5253
PostSolution(workspaceId, projectId, projOutputId, solution string) error

internal/move2kubeapi/handlers/outputs.go

+40
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,46 @@ func HandleReadProjectOutput(w http.ResponseWriter, r *http.Request) {
136136
}
137137
}
138138

139+
// HandleReadProjectOutputGraph handles reading the graph file from the output of a transformation
140+
func HandleReadProjectOutputGraph(w http.ResponseWriter, r *http.Request) {
141+
logrus := GetLogger(r)
142+
logrus.Trace("HandleReadProjectOutputGraph start")
143+
defer logrus.Trace("HandleReadProjectOutputGraph end")
144+
workspaceId := mux.Vars(r)[WORKSPACE_ID_ROUTE_VAR]
145+
projectId := mux.Vars(r)[PROJECT_ID_ROUTE_VAR]
146+
projOutputId := mux.Vars(r)[PROJECT_OUTPUT_ID_ROUTE_VAR]
147+
if !common.IsValidId(workspaceId) || !common.IsValidId(projectId) || !common.IsValidId(projOutputId) {
148+
logrus.Errorf("invalid id. Actual: %s %s %s", workspaceId, projectId, projOutputId)
149+
sendErrorJSON(w, "invalid id", http.StatusBadRequest)
150+
return
151+
}
152+
_, file, err := m2kFS.ReadProjectOutputGraph(workspaceId, projectId, projOutputId)
153+
if err != nil {
154+
logrus.Errorf("failed to get the project output. Error: %q", err)
155+
if _, ok := err.(types.ErrorDoesNotExist); ok {
156+
w.WriteHeader(http.StatusNotFound)
157+
return
158+
}
159+
if e, ok := err.(types.ErrorValidation); ok {
160+
sendErrorJSON(w, e.Reason, http.StatusBadRequest)
161+
return
162+
}
163+
if _, ok := err.(types.ErrorOngoing); ok {
164+
w.WriteHeader(http.StatusNoContent)
165+
return
166+
}
167+
w.WriteHeader(http.StatusInternalServerError)
168+
return
169+
}
170+
w.Header().Set(common.CONTENT_TYPE_HEADER, common.CONTENT_TYPE_JSON)
171+
w.WriteHeader(http.StatusOK)
172+
if _, err := io.Copy(w, file); err != nil {
173+
logrus.Errorf("failed to send the response. Error: %q", err)
174+
w.WriteHeader(http.StatusInternalServerError)
175+
return
176+
}
177+
}
178+
139179
// HandleDeleteProjectOutput handles deleting the output of a transformation
140180
func HandleDeleteProjectOutput(w http.ResponseWriter, r *http.Request) {
141181
logrus := GetLogger(r)

internal/move2kubeapi/move2kubeapi.go

+3
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ func Serve() error {
114114
apiRouter.HandleFunc("/workspaces/{work-id}/projects/{proj-id}/outputs/{output-id}", handlers.HandleReadProjectOutput).Methods("GET")
115115
apiRouter.HandleFunc("/workspaces/{work-id}/projects/{proj-id}/outputs/{output-id}", handlers.HandleDeleteProjectOutput).Methods("DELETE")
116116

117+
// project output graphs
118+
apiRouter.HandleFunc("/workspaces/{work-id}/projects/{proj-id}/outputs/{output-id}/graph", handlers.HandleReadProjectOutputGraph).Methods("GET")
119+
117120
// QA
118121
apiRouter.HandleFunc("/workspaces/{work-id}/projects/{proj-id}/outputs/{output-id}/problems/current", handlers.HandleGetQuestion).Methods("GET")
119122
apiRouter.HandleFunc("/workspaces/{work-id}/projects/{proj-id}/outputs/{output-id}/problems/current/solution", handlers.HandlePostSolution).Methods("POST")

0 commit comments

Comments
 (0)