mirror of
https://github.com/coder/coder.git
synced 2025-07-06 15:41:45 +00:00
In anticipation of needing the `LogSender` to run on a context that doesn't get immediately canceled when you `Close()` the agent, I've undertaken a little refactor to manage the goroutines that get run against the Tailnet and Agent API connection. This handles controlling two contexts, one that gets canceled right away at the start of graceful shutdown, and another that stays up to allow graceful shutdown to complete.
153 lines
4.6 KiB
Go
153 lines
4.6 KiB
Go
package coderd_test
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"fmt"
|
|
"net/http"
|
|
"testing"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"cdr.dev/slog/sloggers/slogtest"
|
|
"github.com/coder/coder/v2/agent"
|
|
"github.com/coder/coder/v2/coderd/coderdtest"
|
|
"github.com/coder/coder/v2/codersdk"
|
|
"github.com/coder/coder/v2/codersdk/agentsdk"
|
|
"github.com/coder/coder/v2/enterprise/coderd/coderdenttest"
|
|
"github.com/coder/coder/v2/enterprise/coderd/license"
|
|
"github.com/coder/coder/v2/provisioner/echo"
|
|
"github.com/coder/coder/v2/provisionersdk/proto"
|
|
"github.com/coder/coder/v2/testutil"
|
|
)
|
|
|
|
// App names for each app sharing level.
|
|
const (
|
|
testAppNameOwner = "test-app-owner"
|
|
testAppNameAuthenticated = "test-app-authenticated"
|
|
testAppNamePublic = "test-app-public"
|
|
)
|
|
|
|
func TestBlockNonBrowser(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("Enabled", func(t *testing.T) {
|
|
t.Parallel()
|
|
client, user := coderdenttest.New(t, &coderdenttest.Options{
|
|
BrowserOnly: true,
|
|
Options: &coderdtest.Options{
|
|
IncludeProvisionerDaemon: true,
|
|
},
|
|
LicenseOptions: &coderdenttest.LicenseOptions{
|
|
Features: license.Features{
|
|
codersdk.FeatureBrowserOnly: 1,
|
|
},
|
|
},
|
|
})
|
|
r := setupWorkspaceAgent(t, client, user, 0)
|
|
//nolint:gocritic // Testing that even the owner gets blocked.
|
|
_, err := client.DialWorkspaceAgent(context.Background(), r.sdkAgent.ID, nil)
|
|
var apiErr *codersdk.Error
|
|
require.ErrorAs(t, err, &apiErr)
|
|
require.Equal(t, http.StatusConflict, apiErr.StatusCode())
|
|
})
|
|
t.Run("Disabled", func(t *testing.T) {
|
|
t.Parallel()
|
|
client, user := coderdenttest.New(t, &coderdenttest.Options{
|
|
Options: &coderdtest.Options{
|
|
IncludeProvisionerDaemon: true,
|
|
},
|
|
LicenseOptions: &coderdenttest.LicenseOptions{
|
|
Features: license.Features{
|
|
codersdk.FeatureBrowserOnly: 0,
|
|
},
|
|
},
|
|
})
|
|
r := setupWorkspaceAgent(t, client, user, 0)
|
|
//nolint:gocritic // Testing RBAC is not the point of this test.
|
|
conn, err := client.DialWorkspaceAgent(context.Background(), r.sdkAgent.ID, nil)
|
|
require.NoError(t, err)
|
|
_ = conn.Close()
|
|
})
|
|
}
|
|
|
|
type setupResp struct {
|
|
workspace codersdk.Workspace
|
|
sdkAgent codersdk.WorkspaceAgent
|
|
agent agent.Agent
|
|
}
|
|
|
|
func setupWorkspaceAgent(t *testing.T, client *codersdk.Client, user codersdk.CreateFirstUserResponse, appPort uint16) setupResp {
|
|
authToken := uuid.NewString()
|
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
|
Parse: echo.ParseComplete,
|
|
ProvisionApply: []*proto.Response{{
|
|
Type: &proto.Response_Apply{
|
|
Apply: &proto.ApplyComplete{
|
|
Resources: []*proto.Resource{{
|
|
Name: "example",
|
|
Type: "aws_instance",
|
|
Agents: []*proto.Agent{{
|
|
Id: uuid.NewString(),
|
|
Name: "example",
|
|
Auth: &proto.Agent_Token{
|
|
Token: authToken,
|
|
},
|
|
Apps: []*proto.App{
|
|
{
|
|
Slug: testAppNameOwner,
|
|
DisplayName: testAppNameOwner,
|
|
SharingLevel: proto.AppSharingLevel_OWNER,
|
|
Url: fmt.Sprintf("http://localhost:%d", appPort),
|
|
},
|
|
{
|
|
Slug: testAppNameAuthenticated,
|
|
DisplayName: testAppNameAuthenticated,
|
|
SharingLevel: proto.AppSharingLevel_AUTHENTICATED,
|
|
Url: fmt.Sprintf("http://localhost:%d", appPort),
|
|
},
|
|
{
|
|
Slug: testAppNamePublic,
|
|
DisplayName: testAppNamePublic,
|
|
SharingLevel: proto.AppSharingLevel_PUBLIC,
|
|
Url: fmt.Sprintf("http://localhost:%d", appPort),
|
|
},
|
|
},
|
|
}},
|
|
}},
|
|
},
|
|
},
|
|
}},
|
|
})
|
|
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
|
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
|
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
|
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID)
|
|
agentClient := agentsdk.New(client.URL)
|
|
agentClient.SDK.HTTPClient = &http.Client{
|
|
Transport: &http.Transport{
|
|
TLSClientConfig: &tls.Config{
|
|
//nolint:gosec
|
|
InsecureSkipVerify: true,
|
|
},
|
|
},
|
|
}
|
|
agentClient.SetSessionToken(authToken)
|
|
agnt := agent.New(agent.Options{
|
|
Client: agentClient,
|
|
Logger: slogtest.Make(t, nil).Named("agent"),
|
|
})
|
|
t.Cleanup(func() {
|
|
_ = agnt.Close()
|
|
})
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
|
defer cancel()
|
|
|
|
resources := coderdtest.AwaitWorkspaceAgents(t, client, workspace.ID)
|
|
sdkAgent, err := client.WorkspaceAgent(ctx, resources[0].Agents[0].ID)
|
|
require.NoError(t, err)
|
|
|
|
return setupResp{workspace, sdkAgent, agnt}
|
|
}
|