Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
This repository was archived by the owner on Aug 30, 2024. It is now read-only.
/coder-v1-cliPublic archive

Add coder envs rebuild and watch-build commands#146

Merged
cmoog merged 1 commit intomasterfromenvs-rebuild
Oct 20, 2020
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletionscoder-sdk/env.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -122,6 +122,29 @@ func (c Client) StopEnvironment(ctx context.Context, envID string) error {
return c.requestBody(ctx, http.MethodPut, "/api/environments/"+envID+"/stop", nil, nil)
}

// UpdateEnvironmentReq defines the update operation, only setting
// nil-fields.
type UpdateEnvironmentReq struct {
ImageID *string `json:"image_id"`
ImageTag *string `json:"image_tag"`
CPUCores *float32 `json:"cpu_cores"`
MemoryGB *float32 `json:"memory_gb"`
DiskGB *int `json:"disk_gb"`
GPUs *int `json:"gpus"`
Services *[]string `json:"services"`
CodeServerReleaseURL *string `json:"code_server_release_url"`
}

// RebuildEnvironment requests that the given envID is rebuilt with no changes to its specification.
func (c Client) RebuildEnvironment(ctx context.Context, envID string) error {
return c.requestBody(ctx, http.MethodPatch, "/api/environments/"+envID, UpdateEnvironmentReq{}, nil)
}

// EditEnvironment modifies the environment specification and initiates a rebuild.
func (c Client) EditEnvironment(ctx context.Context, envID string, req UpdateEnvironmentReq) error {
return c.requestBody(ctx, http.MethodPatch, "/api/environments/"+envID, req, nil)
}

// DialWsep dials an environments command execution interface
// See https://github.com/cdr/wsep for details.
func (c Client) DialWsep(ctx context.Context, env *Environment) (*websocket.Conn, error) {
Expand All@@ -138,6 +161,49 @@ func (c Client) DialEnvironmentBuildLog(ctx context.Context, envID string) (*web
return c.dialWebsocket(ctx, "/api/environments/"+envID+"/watch-update")
}

// BuildLog defines a build log record for a Coder environment.
type BuildLog struct {
ID string `db:"id" json:"id"`
EnvironmentID string `db:"environment_id" json:"environment_id"`
// BuildID allows the frontend to separate the logs from the old build with the logs from the new.
BuildID string `db:"build_id" json:"build_id"`
Time time.Time `db:"time" json:"time"`
Type BuildLogType `db:"type" json:"type"`
Msg string `db:"msg" json:"msg"`
}

// BuildLogFollowMsg wraps the base BuildLog and adds a field for collecting
// errors that may occur when follow or parsing.
type BuildLogFollowMsg struct {
BuildLog
Err error
}

// FollowEnvironmentBuildLog trails the build log of a Coder environment.
func (c Client) FollowEnvironmentBuildLog(ctx context.Context, envID string) (<-chan BuildLogFollowMsg, error) {
ch := make(chan BuildLogFollowMsg)
ws, err := c.DialEnvironmentBuildLog(ctx, envID)
Comment on lines +175 to +185
Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

@f0ssel@coadler Any thoughts on this pattern? Seems better to me than having aBuildLogWatcher type with methods for iterating when channels are designed for that.

if err != nil {
return nil, err
}
go func() {
defer ws.Close(websocket.StatusNormalClosure, "normal closure")
defer close(ch)
for {
var msg BuildLog
if err := wsjson.Read(ctx, ws, &msg); err != nil {
ch <- BuildLogFollowMsg{Err: err}
if xerrors.Is(err, context.Canceled) || xerrors.Is(err, context.DeadlineExceeded) {
return
}
continue
}
ch <- BuildLogFollowMsg{BuildLog: msg}
}
}()
return ch, nil
}

// DialEnvironmentStats opens a websocket connection for environment stats.
func (c Client) DialEnvironmentStats(ctx context.Context, envID string) (*websocket.Conn, error) {
return c.dialWebsocket(ctx, "/api/environments/"+envID+"/watch-stats")
Expand Down
7 changes: 4 additions & 3 deletionsgo.mod
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,20 +5,21 @@ go 1.14
require (
cdr.dev/slog v1.3.0
cdr.dev/wsep v0.0.0-20200728013649-82316a09813f
github.com/fatih/color v1.9.0 // indirect
github.com/briandowns/spinner v1.11.1
github.com/fatih/color v1.9.0
github.com/gorilla/websocket v1.4.1
github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f
github.com/klauspost/compress v1.10.8 // indirect
github.com/manifoldco/promptui v0.7.0
github.com/mattn/go-colorable v0.1.6 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4
github.com/rjeczalik/notify v0.9.2
github.com/spf13/cobra v1.0.0
github.com/stretchr/testify v1.6.1
go.coder.com/flog v0.0.0-20190906214207-47dd47ea0512
golang.org/x/crypto v0.0.0-20200422194213-44a606286825
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13
golang.org/x/time v0.0.0-20191024005414-555d28b269f0
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
nhooyr.io/websocket v1.8.6
Expand Down
11 changes: 7 additions & 4 deletionsgo.sum
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,6 +38,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/briandowns/spinner v1.11.1 h1:OixPqDEcX3juo5AjQZAnFPbeUA0jvkp2qzB5gOZJ/L0=
github.com/briandowns/spinner v1.11.1/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX3FScO+3/ZPQ=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
Expand DownExpand Up@@ -176,10 +178,11 @@ github.com/manifoldco/promptui v0.7.0 h1:3l11YT8tm9MnwGFQ4kETwkzpAwY2Jt9lCrumCUW
github.com/manifoldco/promptui v0.7.0/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEXGwov2nIKuGQ=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
Expand DownExpand Up@@ -334,8 +337,8 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13 h1:5jaG59Zhd+8ZXe8C+lgiAGqkOaZBruqrWclLkgAww34=
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
Expand Down
2 changes: 2 additions & 0 deletionsinternal/cmd/envs.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -64,6 +64,8 @@ func envsCommand() *cobra.Command {
cmd.AddCommand(lsCmd)
cmd.AddCommand(stopEnvCommand(&user))

cmd.AddCommand(watchBuildLogCommand())
cmd.AddCommand(rebuildEnvCommand())
return cmd
}

Expand Down
146 changes: 146 additions & 0 deletionsinternal/cmd/rebuild.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
package cmd

import (
"context"
"fmt"
"strings"
"time"

"cdr.dev/coder-cli/coder-sdk"
"github.com/briandowns/spinner"
"github.com/fatih/color"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"go.coder.com/flog"
"golang.org/x/xerrors"
)

func rebuildEnvCommand() *cobra.Command {
var follow bool
var force bool
cmd := &cobra.Command{
Use: "rebuild [environment_name]",
Short: "rebuild a Coder environment",
Args: cobra.ExactArgs(1),
Example: `coder envs rebuild front-end-env --follow
coder envs rebuild backend-env --force`,
Hidden: true, // TODO(@cmoog) un-hide
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
client, err := newClient()
if err != nil {
return err
}
env, err := findEnv(ctx, client, args[0], coder.Me)
if err != nil {
return err
}

if !force && env.LatestStat.ContainerStatus == coder.EnvironmentOn {
_, err = (&promptui.Prompt{
Label: fmt.Sprintf("Rebuild environment \"%s\"? (will destroy any work outside of /home)", env.Name),
IsConfirm: true,
}).Run()
if err != nil {
return err
}
}

if err = client.RebuildEnvironment(ctx, env.ID); err != nil {
return err
}
if follow {
if err = trailBuildLogs(ctx, client, env.ID); err != nil {
return err
}
} else {
flog.Info("Use \"coder envs watch-build %s\" to follow the build logs", env.Name)
}
return nil
},
}

cmd.Flags().BoolVar(&follow, "follow", false, "follow buildlog after initiating rebuild")
cmd.Flags().BoolVar(&force, "force", false, "force rebuild without showing a confirmation prompt")
return cmd
}

// trailBuildLogs follows the build log for a given environment and prints the staged
// output with loaders and success/failure indicators for each stage
func trailBuildLogs(ctx context.Context, client *coder.Client, envID string) error {
const check = "✅"
const failure = "❌"
const loading = "⌛"

newSpinner := func() *spinner.Spinner { return spinner.New(spinner.CharSets[11], 100*time.Millisecond) }

logs, err := client.FollowEnvironmentBuildLog(ctx, envID)
if err != nil {
return err
}
var s *spinner.Spinner
for l := range logs {
if l.Err != nil {
return l.Err
}
switch l.BuildLog.Type {
case coder.BuildLogTypeStart:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Do we need to do anything for this case?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

The FE uses this to reset the UI logs, but since we're just trailing I don't think it makes sense to do anything. Plus we exit after the Done message is sent anyway.

fuskovic reacted with thumbs up emoji
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

That makes sense.

I think that explanation should be plugged in as a comment.

cmoog reacted with thumbs up emoji
// the FE uses this to reset the UI
// the CLI doesn't need to do anything here given that we only append to the trail
case coder.BuildLogTypeStage:
if s != nil {
s.Stop()
fmt.Print("\n")
}
s = newSpinner()
msg := fmt.Sprintf("%s %s", l.BuildLog.Time.Format(time.RFC3339), l.BuildLog.Msg)
s.Suffix = fmt.Sprintf(" -- %s", msg)
s.FinalMSG = fmt.Sprintf("%s -- %s", check, msg)
s.Start()
case coder.BuildLogTypeSubstage:
// TODO(@cmoog) add verbose substage printing
case coder.BuildLogTypeError:
if s != nil {
s.FinalMSG = fmt.Sprintf("%s %s", failure, strings.TrimPrefix(s.Suffix, " "))
s.Stop()
}
fmt.Print(color.RedString("\t%s", l.BuildLog.Msg))
s = newSpinner()
case coder.BuildLogTypeDone:
if s != nil {
s.Stop()
}
return nil
default:
return xerrors.Errorf("unknown buildlog type: %s", l.BuildLog.Type)
}
}
return nil
}

func watchBuildLogCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "watch-build [environment_name]",
Example: "coder watch-build front-end-env",
Short: "trail the build log of a Coder environment",
Args: cobra.ExactArgs(1),
Hidden: true, // TODO(@cmoog) un-hide
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
client, err := newClient()
if err != nil {
return err
}
env, err := findEnv(ctx, client, args[0], coder.Me)
if err != nil {
return err
}

if err = trailBuildLogs(ctx, client, env.ID); err != nil {
return err
}
return nil
},
}
return cmd
}

[8]ページ先頭

©2009-2025 Movatter.jp