Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
Prev Previous commit
Next Next commit
Rename workspace agent to agent
  • Loading branch information
kylecarbs committed Feb 28, 2022
commit 07f21aa69308cee830785286d86e6007b64728cb
5 changes: 3 additions & 2 deletions cli/workspaceagent.go → cli/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import (
"github.com/spf13/cobra"
)

func workspaceAgent() *cobra.Command {
func agent() *cobra.Command {
return &cobra.Command{
Use: "agent",
Use: "agent",
Hidden: true,
RunE: func(cmd *cobra.Command, args []string) error {
return nil
},
Expand Down
1 change: 1 addition & 0 deletions cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ func Root() *cobra.Command {
`Additional help topics:`, header.Sprint("Additional help:"),
).Replace(cmd.UsageTemplate()))

cmd.AddCommand(agent())
cmd.AddCommand(login())
cmd.AddCommand(projects())
cmd.AddCommand(workspaces())
Expand Down
1 change: 0 additions & 1 deletion cli/workspaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ func workspaces() *cobra.Command {
cmd := &cobra.Command{
Use: "workspaces",
}
cmd.AddCommand(workspaceAgent())
cmd.AddCommand(workspaceCreate())

return cmd
Expand Down
50 changes: 8 additions & 42 deletions coderd/workspaceagent.go → coderd/agent.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
package coderd

import (
"database/sql"
"fmt"
"io"
"net/http"
"time"

"github.com/hashicorp/yamux"
"nhooyr.io/websocket"

"cdr.dev/slog"
"github.com/coder/coder/database"
"github.com/coder/coder/httpapi"
"github.com/coder/coder/httpmw"
"github.com/coder/coder/peerbroker"
Expand Down Expand Up @@ -39,15 +35,15 @@ type AgentInstanceMetadata struct {
VNC bool `json:"vnc"`
}

func (api *api) workspaceAgentUpdate() {
func (api *api) agentUpdate() {

}

func (api *api) workspaceAgentConnectByResource(rw http.ResponseWriter, r *http.Request) {
func (api *api) agentConnectByResource(rw http.ResponseWriter, r *http.Request) {
api.websocketWaitGroup.Add(1)
defer api.websocketWaitGroup.Done()

agent := httpmw.WorkspaceAgent(r)
agent := httpmw.Agent(r)
if !agent.UpdatedAt.Valid {
httpapi.Write(rw, http.StatusPreconditionRequired, httpapi.Response{
Message: "Agent hasn't connected yet!",
Expand Down Expand Up @@ -75,7 +71,7 @@ func (api *api) workspaceAgentConnectByResource(rw http.ResponseWriter, r *http.
return
}
err = peerbroker.ProxyListen(r.Context(), session, peerbroker.ProxyOptions{
ChannelID: resource.WorkspaceAgentID.UUID.String(),
ChannelID: agent.ID.String(),
Logger: api.Logger.Named("peerbroker-proxy-dial"),
Pubsub: api.Pubsub,
})
Expand All @@ -85,11 +81,11 @@ func (api *api) workspaceAgentConnectByResource(rw http.ResponseWriter, r *http.
}
}

func (api *api) workspaceAgentServe(rw http.ResponseWriter, r *http.Request) {
func (api *api) agentServe(rw http.ResponseWriter, r *http.Request) {
api.websocketWaitGroup.Add(1)
defer api.websocketWaitGroup.Done()

workspaceAgent := httpmw.WorkspaceAgent(r)
agent := httpmw.Agent(r)
conn, err := websocket.Accept(rw, r, &websocket.AcceptOptions{
CompressionMode: websocket.CompressionDisabled,
})
Expand All @@ -110,44 +106,14 @@ func (api *api) workspaceAgentServe(rw http.ResponseWriter, r *http.Request) {
return
}
closer, err := peerbroker.ProxyDial(proto.NewDRPCPeerBrokerClient(provisionersdk.Conn(session)), peerbroker.ProxyOptions{
ChannelID: workspaceAgent.ID.String(),
ChannelID: agent.ID.String(),
Pubsub: api.Pubsub,
Logger: api.Logger.Named("peerbroker-proxy-listen"),
})
if err != nil {
_ = conn.Close(websocket.StatusAbnormalClosure, err.Error())
return
}

err = api.Database.UpdateWorkspaceAgentByID(r.Context(), database.UpdateWorkspaceAgentByIDParams{
ID: workspaceAgent.ID,
UpdatedAt: sql.NullTime{
Time: database.Now(),
Valid: true,
},
})
if err != nil {
_ = conn.Close(websocket.StatusAbnormalClosure, err.Error())
return
}
defer closer.Close()
ticker := time.NewTicker(5 * time.Second)
for {
select {
case <-ticker.C:
err = api.Database.UpdateWorkspaceAgentByID(r.Context(), database.UpdateWorkspaceAgentByIDParams{
ID: workspaceAgent.ID,
UpdatedAt: sql.NullTime{
Time: database.Now(),
Valid: true,
},
})
if err != nil {
api.Logger.Error(r.Context(), "update workspace agent by id", slog.Error(err), slog.F("id", workspaceAgent.ID.String()))
return
}
case <-r.Context().Done():
return
}
}
<-r.Context().Done()
}
8 changes: 4 additions & 4 deletions coderd/workspaceagentauth.go → coderd/agentauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,16 @@ type GoogleInstanceIdentityToken struct {
JSONWebToken string `json:"json_web_token" validate:"required"`
}

// WorkspaceAgentAuthenticateResponse is returned when an instance ID
// AgentAuthResponse is returned when an instance ID
// has been exchanged for a session token.
type WorkspaceAgentAuthenticateResponse struct {
type AgentAuthResponse struct {
SessionToken string `json:"session_token"`
}

// Google Compute Engine supports instance identity verification:
// https://cloud.google.com/compute/docs/instances/verifying-instance-identity
// Using this, we can exchange a signed instance payload for an agent token.
func (api *api) postAuthenticateWorkspaceAgentUsingGoogleInstanceIdentity(rw http.ResponseWriter, r *http.Request) {
func (api *api) postAuthenticateAgentUsingGoogleInstanceIdentity(rw http.ResponseWriter, r *http.Request) {
var req GoogleInstanceIdentityToken
if !httpapi.Read(rw, r, &req) {
return
Expand Down Expand Up @@ -121,7 +121,7 @@ func (api *api) postAuthenticateWorkspaceAgentUsingGoogleInstanceIdentity(rw htt
return
}
render.Status(r, http.StatusOK)
render.JSON(rw, r, WorkspaceAgentAuthenticateResponse{
render.JSON(rw, r, AgentAuthResponse{
SessionToken: agent.AuthToken.String(),
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (
"github.com/coder/coder/provisionersdk/proto"
)

func TestPostWorkspaceAgentAuthenticateGoogleInstanceIdentity(t *testing.T) {
func TestPostAgentAuthenticateGoogleInstanceIdentity(t *testing.T) {
t.Parallel()
t.Run("Expired", func(t *testing.T) {
t.Parallel()
Expand All @@ -38,7 +38,7 @@ func TestPostWorkspaceAgentAuthenticateGoogleInstanceIdentity(t *testing.T) {
client := coderdtest.New(t, &coderdtest.Options{
GoogleTokenValidator: validator,
})
_, err := client.AuthenticateWorkspaceAgentUsingGoogleCloudIdentity(context.Background(), "", createMetadataClient(signedKey))
_, err := client.AuthenticateAgentWithGoogleCloudIdentity(context.Background(), "", createMetadataClient(signedKey))
var apiErr *codersdk.Error
require.ErrorAs(t, err, &apiErr)
require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode())
Expand All @@ -52,7 +52,7 @@ func TestPostWorkspaceAgentAuthenticateGoogleInstanceIdentity(t *testing.T) {
client := coderdtest.New(t, &coderdtest.Options{
GoogleTokenValidator: validator,
})
_, err := client.AuthenticateWorkspaceAgentUsingGoogleCloudIdentity(context.Background(), "", createMetadataClient(signedKey))
_, err := client.AuthenticateAgentWithGoogleCloudIdentity(context.Background(), "", createMetadataClient(signedKey))
var apiErr *codersdk.Error
require.ErrorAs(t, err, &apiErr)
require.Equal(t, http.StatusNotFound, apiErr.StatusCode())
Expand Down Expand Up @@ -98,7 +98,7 @@ func TestPostWorkspaceAgentAuthenticateGoogleInstanceIdentity(t *testing.T) {
require.NoError(t, err)
coderdtest.AwaitWorkspaceProvisionJob(t, client, user.Organization, firstHistory.ProvisionJobID)

_, err = client.AuthenticateWorkspaceAgentUsingGoogleCloudIdentity(context.Background(), "", createMetadataClient(signedKey))
_, err = client.AuthenticateAgentWithGoogleCloudIdentity(context.Background(), "", createMetadataClient(signedKey))
require.NoError(t, err)
})
}
Expand Down
4 changes: 2 additions & 2 deletions coderd/coderd.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,9 @@ func New(options *Options) (http.Handler, func()) {
})
})

r.Route("/workspaceagent", func(r chi.Router) {
r.Route("/agent", func(r chi.Router) {
r.Route("/authenticate", func(r chi.Router) {
r.Post("/google-instance-identity", api.postAuthenticateWorkspaceAgentUsingGoogleInstanceIdentity)
r.Post("/google-instance-identity", api.postAuthenticateAgentUsingGoogleInstanceIdentity)
})
})

Expand Down
14 changes: 7 additions & 7 deletions codersdk/workspaceagent.go → codersdk/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import (
"github.com/coder/coder/coderd"
)

// AuthenticateWorkspaceAgentUsingGoogleCloudIdentity uses the Google Compute Engine Metadata API to
// AuthenticateAgentWithGoogleCloudIdentity uses the Google Compute Engine Metadata API to
// fetch a signed JWT, and exchange it for a session token for a workspace agent.
//
// The requesting instance must be registered as a resource in the latest history for a workspace.
func (c *Client) AuthenticateWorkspaceAgentUsingGoogleCloudIdentity(ctx context.Context, serviceAccount string, gcpClient *metadata.Client) (coderd.WorkspaceAgentAuthenticateResponse, error) {
func (c *Client) AuthenticateAgentWithGoogleCloudIdentity(ctx context.Context, serviceAccount string, gcpClient *metadata.Client) (coderd.AgentAuthResponse, error) {
if serviceAccount == "" {
// This is the default name specified by Google.
serviceAccount = "default"
Expand All @@ -27,18 +27,18 @@ func (c *Client) AuthenticateWorkspaceAgentUsingGoogleCloudIdentity(ctx context.
// "format=full" is required, otherwise the responding payload will be missing "instance_id".
jwt, err := gcpClient.Get(fmt.Sprintf("instance/service-accounts/%s/identity?audience=coder&format=full", serviceAccount))
if err != nil {
return coderd.WorkspaceAgentAuthenticateResponse{}, xerrors.Errorf("get metadata identity: %w", err)
return coderd.AgentAuthResponse{}, xerrors.Errorf("get metadata identity: %w", err)
}
res, err := c.request(ctx, http.MethodPost, "/api/v2/workspaceagent/authenticate/google-instance-identity", coderd.GoogleInstanceIdentityToken{
res, err := c.request(ctx, http.MethodPost, "/api/v2/agent/authenticate/google-instance-identity", coderd.GoogleInstanceIdentityToken{
JSONWebToken: jwt,
})
if err != nil {
return coderd.WorkspaceAgentAuthenticateResponse{}, err
return coderd.AgentAuthResponse{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return coderd.WorkspaceAgentAuthenticateResponse{}, readBodyAsError(res)
return coderd.AgentAuthResponse{}, readBodyAsError(res)
}
var resp coderd.WorkspaceAgentAuthenticateResponse
var resp coderd.AgentAuthResponse
return resp, json.NewDecoder(res.Body).Decode(&resp)
}
4 changes: 2 additions & 2 deletions codersdk/workspaceagent_test.go → codersdk/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ import (
"github.com/coder/coder/coderd/coderdtest"
)

func TestAuthenticateWorkspaceAgentUsingGoogleCloudIdentity(t *testing.T) {
func TestAuthenticateAgentUsingGoogleCloudIdentity(t *testing.T) {
t.Parallel()
t.Run("Error", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
_, err := client.AuthenticateWorkspaceAgentUsingGoogleCloudIdentity(context.Background(), "", metadata.NewClient(&http.Client{
_, err := client.AuthenticateAgentWithGoogleCloudIdentity(context.Background(), "", metadata.NewClient(&http.Client{
Transport: roundTripper(func(req *http.Request) (*http.Response, error) {
return &http.Response{
StatusCode: http.StatusOK,
Expand Down
21 changes: 11 additions & 10 deletions httpmw/workspaceagent.go → httpmw/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,25 @@ import (
"fmt"
"net/http"

"github.com/google/uuid"

"github.com/coder/coder/database"
"github.com/coder/coder/httpapi"
"github.com/google/uuid"
)

type workspaceAgentContextKey struct{}
type agentContextKey struct{}

// WorkspaceAgent returns the workspace agent from the ExtractWorkspaceAgent handler.
func WorkspaceAgent(r *http.Request) database.ProvisionerJobAgent {
user, ok := r.Context().Value(workspaceAgentContextKey{}).(database.ProvisionerJobAgent)
// Agent returns the workspace agent from the ExtractAgent handler.
func Agent(r *http.Request) database.ProvisionerJobAgent {
user, ok := r.Context().Value(agentContextKey{}).(database.ProvisionerJobAgent)
if !ok {
panic("developer error: workspace agent middleware not provided")
panic("developer error: agent middleware not provided")
}
return user
}

// ExtractWorkspaceAgent requires authentication using a valid agent token.
func ExtractWorkspaceAgent(db database.Store) func(http.Handler) http.Handler {
// ExtractAgent requires authentication using a valid agent token.
func ExtractAgent(db database.Store) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
cookie, err := r.Cookie(AuthCookie)
Expand All @@ -41,7 +42,7 @@ func ExtractWorkspaceAgent(db database.Store) func(http.Handler) http.Handler {
})
return
}
workspaceAgent, err := db.GetProvisionerJobAgentByAuthToken(r.Context(), token)
agent, err := db.GetProvisionerJobAgentByAuthToken(r.Context(), token)
if errors.Is(err, sql.ErrNoRows) {
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Expand All @@ -57,7 +58,7 @@ func ExtractWorkspaceAgent(db database.Store) func(http.Handler) http.Handler {
return
}

ctx := context.WithValue(r.Context(), workspaceAgentContextKey{}, workspaceAgent)
ctx := context.WithValue(r.Context(), agentContextKey{}, agent)
next.ServeHTTP(rw, r.WithContext(ctx))
})
}
Expand Down
8 changes: 4 additions & 4 deletions httpmw/workspaceagent_test.go → httpmw/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
"github.com/coder/coder/httpmw"
)

func TestWorkspaceAgent(t *testing.T) {
func TestAgent(t *testing.T) {
t.Parallel()

setup := func(db database.Store) (*http.Request, uuid.UUID) {
Expand All @@ -33,7 +33,7 @@ func TestWorkspaceAgent(t *testing.T) {
db := databasefake.New()
rtr := chi.NewRouter()
rtr.Use(
httpmw.ExtractWorkspaceAgent(db),
httpmw.ExtractAgent(db),
)
rtr.Get("/", nil)
r, _ := setup(db)
Expand All @@ -50,10 +50,10 @@ func TestWorkspaceAgent(t *testing.T) {
db := databasefake.New()
rtr := chi.NewRouter()
rtr.Use(
httpmw.ExtractWorkspaceAgent(db),
httpmw.ExtractAgent(db),
)
rtr.Get("/", func(rw http.ResponseWriter, r *http.Request) {
_ = httpmw.WorkspaceAgent(r)
_ = httpmw.Agent(r)
rw.WriteHeader(http.StatusOK)
})
r, token := setup(db)
Expand Down