Skip to content

Commit

Permalink
Wait for kernel builds to be complete before exiting codex upload cmd
Browse files Browse the repository at this point in the history
  • Loading branch information
twavv committed Aug 9, 2021
1 parent d9c741e commit d0a9642
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 12 deletions.
78 changes: 67 additions & 11 deletions cmd/codex/upload.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
package codex

import (
"context"
"fmt"
"github.com/fatih/color"
"github.com/pathbird/pbauthor/internal/api"
"github.com/pathbird/pbauthor/internal/auth"
"github.com/pathbird/pbauthor/internal/codex"
"github.com/pathbird/pbauthor/internal/config"
"github.com/pathbird/pbauthor/internal/graphql"
"github.com/pathbird/pbauthor/internal/prompt"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"os"
"path/filepath"
"time"
)

// flag vars
var (
skipConfirmation bool
noWait bool
)

var codexUploadCmd = &cobra.Command{
Expand Down Expand Up @@ -44,7 +48,7 @@ var codexUploadCmd = &cobra.Command{
// the name of the codex and the course it's being uploaded to.
if !skipConfirmation {
if !prompt.Confirm("Upload codex?") {
_, _ = fmt.Fprintln(os.Stderr, red("Upload aborted."))
_, _ = fmt.Fprintln(os.Stderr, failf("Upload aborted."))
os.Exit(1)
}
}
Expand All @@ -57,9 +61,13 @@ var codexUploadCmd = &cobra.Command{
return err
}
if parseErr != nil {
_, _ = fmt.Fprintf(os.Stderr, "Failed to parse codex (%d issues):\n", len(parseErr.Errors))
_, _ = fmt.Fprintf(
os.Stderr,
"Failed to parse codex (%d issues):\n",
len(parseErr.Errors),
)
for _, e := range parseErr.Errors {
_, _ = fmt.Fprintf(os.Stderr, "- %s\n (%s", red(e.Message), blue(e.Error))
_, _ = fmt.Fprintf(os.Stderr, "- %s\n (%s", failf(e.Message), blue(e.Error))
if e.SourcePosition != "" {
_, _ = fmt.Fprintf(os.Stderr, " at %s", cyan(e.SourcePosition))
}
Expand All @@ -73,21 +81,69 @@ var codexUploadCmd = &cobra.Command{
os.Exit(1)
}

fmt.Printf("codexId: %s\n", res.CodexId)
fmt.Printf("url: %s/codex/%s\n", config.PathbirdApiHost, res.CodexId)
detailsUrl := fmt.Sprintf("https://pathbird.com/codex/%s/details", res.CodexId)

if !noWait {
start := time.Now()
timeoutCtx, cancel := context.WithTimeout(context.Background(), 20*time.Minute)
defer cancel()
log.Info("waiting for kernel build to complete (this may take up to 20 minutes)...")
kernelStatus, err := codex.WaitForKernelBuildCompleted(
timeoutCtx,
graphql.NewClient(auth),
res.CodexId,
)
if err != nil {
log.WithError(
err,
).Error(
"Something went wrong while trying to check the status of the codex.",
)
return err
}
log.Infof("waited %s for kernel build process", time.Since(start))
if kernelStatus.BuildStatus != "built" {
_, _ = fmt.Fprint(os.Stderr, failf(
"Failed to build kernel (got status: %s): %s\n",
kernelStatus.BuildStatus,
detailsUrl,
))
return errors.Errorf(
"failed to build kernel (got status: %s)",
kernelStatus.BuildStatus,
)
}
} else {
log.Info("not waiting for kernel build to complete (--no-wait was set)")
}

fmt.Printf(successf("Successfully uploaded codex: %s", detailsUrl))

return nil
},
}

func init() {
codexUploadCmd.Flags().BoolVarP(&skipConfirmation, "yes", "y", false, "don't ask for confirmation")
codexUploadCmd.Flags().BoolVarP(
&skipConfirmation,
"yes",
"y",
false,
"don't ask for confirmation",
)
codexUploadCmd.Flags().BoolVar(
&noWait,
"no-wait",
false,
"don't wait for the kernel build process to complete",
)
Cmd.AddCommand(codexUploadCmd)
}

var (
red = color.New(color.FgRed, color.Bold).SprintFunc()
cyan = color.New(color.FgCyan).SprintFunc()
faint = color.New(color.Faint).SprintFunc()
blue = color.New(color.FgBlue).SprintfFunc()
failf = color.New(color.FgRed, color.Bold).SprintfFunc()
successf = color.New(color.FgGreen, color.Bold).SprintfFunc()
cyan = color.New(color.FgCyan).SprintFunc()
faint = color.New(color.Faint).SprintFunc()
blue = color.New(color.FgBlue).SprintfFunc()
)
10 changes: 9 additions & 1 deletion internal/api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ func New(authToken string) *Client {
}
}

func (c *Client) Auth() string {
return c.authToken
}

type request struct {
route string
body interface{}
Expand Down Expand Up @@ -117,7 +121,11 @@ func (r *response) unmarshalErrorBody() (*ErrorResponse, error) {
Details: nil,
}, nil
}
return nil, errors.Errorf("unknown api error response (status: %s, content-type: %s)", r.httpResponse.Status, contentType)
return nil, errors.Errorf(
"unknown api error response (status: %s, content-type: %s)",
r.httpResponse.Status,
contentType,
)
}

var errorResponse ErrorResponse
Expand Down
File renamed without changes.
89 changes: 89 additions & 0 deletions internal/codex/details.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package codex

import (
"context"
"github.com/pathbird/pbauthor/internal/graphql"
"github.com/pathbird/pbauthor/internal/graphql/transport"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
time "time"
)

type Details struct {
Name string
KernelSpec KernelSpec `json:"kernelSpec"`
}

type KernelSpec struct {
ID string `json:"id"`
BuildStatus string `json:"buildStatus"`
Events []string `json:"events"`
BuildLog []string `json:"buildLog"`
}

const waitForBuildStatusQuery = `
query pbauthor_CodexBuildStatus($id: ID!) {
node(id: $id) { ... on CodexMetadata {
id
name
kernelSpec {
id
buildStatus
events
buildLog
}
}}
}
`

// WaitForKernelBuildCompleted polls the API server until the kernel build is completed.
func WaitForKernelBuildCompleted(
ctx context.Context,
client *graphql.Client,
codexId string,
) (*KernelSpec, error) {
for {
log.WithField("codex_id", codexId).Debug("querying kernel build status")
status, err := queryKernelStatus(ctx, client, codexId)
if err != nil {
return nil, err
}
if status.BuildStatus != "pending" {
return status, nil
}

// sleep for a few seconds, then try again
select {
case <-time.After(5 * time.Second):
// pass
case <-ctx.Done():
return nil, ctx.Err()
}
}
}

func queryKernelStatus(
ctx context.Context,
client *graphql.Client,
codexId string,
) (*KernelSpec, error) {
req := transport.NewRequest(waitForBuildStatusQuery)
req.Var("id", codexId)
var res struct {
Node struct {
ID string
Name string
KernelSpec KernelSpec
}
}
if err := client.Run(ctx, req, &res); err != nil {
return nil, err
}
if res.Node.ID == "" {
return nil, errors.Errorf("codex (id: %s) could not be found", codexId)
}
if res.Node.KernelSpec.ID == "" {
return nil, errors.Errorf("query didn't return KernelSpec data (for codex: %s)", codexId)
}
return &res.Node.KernelSpec, nil
}

0 comments on commit d0a9642

Please sign in to comment.