chore: make default workspace proxy editable (#7903)

* chore: add editing the default workspace proxy
This commit is contained in:
Steven Masley
2023-06-08 10:30:15 -05:00
committed by GitHub
parent 5e647ba07a
commit fa8153a0fd
20 changed files with 453 additions and 52 deletions

View File

@ -369,6 +369,11 @@ func (q *querier) DeleteLicense(ctx context.Context, id int32) (int32, error) {
return id, nil
}
func (q *querier) GetDefaultProxyConfig(ctx context.Context) (database.GetDefaultProxyConfigRow, error) {
// No authz checks
return q.db.GetDefaultProxyConfig(ctx)
}
func (q *querier) GetDeploymentID(ctx context.Context) (string, error) {
// No authz checks
return q.db.GetDeploymentID(ctx)

View File

@ -335,6 +335,12 @@ func (s *MethodTestSuite) TestLicense() {
s.Run("GetDeploymentID", s.Subtest(func(db database.Store, check *expects) {
check.Args().Asserts().Returns("")
}))
s.Run("GetDefaultProxyConfig", s.Subtest(func(db database.Store, check *expects) {
check.Args().Asserts().Returns(database.GetDefaultProxyConfigRow{
DisplayName: "Default",
IconUrl: "/emojis/1f3e1.png",
})
}))
s.Run("GetLogoURL", s.Subtest(func(db database.Store, check *expects) {
err := db.UpsertLogoURL(context.Background(), "value")
require.NoError(s.T(), err)

View File

@ -431,3 +431,10 @@ func (q *querier) GetWorkspaceProxyByHostname(ctx context.Context, params databa
}
return q.db.GetWorkspaceProxyByHostname(ctx, params)
}
func (q *querier) UpsertDefaultProxy(ctx context.Context, arg database.UpsertDefaultProxyParams) error {
if err := q.authorizeContext(ctx, rbac.ActionUpdate, rbac.ResourceSystem); err != nil {
return err
}
return q.db.UpsertDefaultProxy(ctx, arg)
}

View File

@ -25,6 +25,9 @@ func (s *MethodTestSuite) TestSystemFunctions() {
LoginType: database.LoginTypeGithub,
}).Asserts(rbac.ResourceSystem, rbac.ActionUpdate).Returns(l)
}))
s.Run("UpsertDefaultProxy", s.Subtest(func(db database.Store, check *expects) {
check.Args(database.UpsertDefaultProxyParams{}).Asserts(rbac.ResourceSystem, rbac.ActionUpdate).Returns()
}))
s.Run("GetUserLinkByLinkedID", s.Subtest(func(db database.Store, check *expects) {
l := dbgen.UserLink(s.T(), db, database.UserLink{})
check.Args(l.LinkedID).Asserts(rbac.ResourceSystem, rbac.ActionRead).Returns(l)

View File

@ -41,7 +41,7 @@ var errDuplicateKey = &pq.Error{
// New returns an in-memory fake of the database.
func New() database.Store {
return &fakeQuerier{
q := &fakeQuerier{
mutex: &sync.RWMutex{},
data: &data{
apiKeys: make([]database.APIKey, 0),
@ -73,6 +73,9 @@ func New() database.Store {
locks: map[int64]struct{}{},
},
}
q.defaultProxyDisplayName = "Default"
q.defaultProxyIconURL = "/emojis/1f3e1.png"
return q
}
type rwMutex interface {
@ -144,14 +147,16 @@ type data struct {
// Locks is a map of lock names. Any keys within the map are currently
// locked.
locks map[int64]struct{}
deploymentID string
derpMeshKey string
lastUpdateCheck []byte
serviceBanner []byte
logoURL string
appSecurityKey string
lastLicenseID int32
locks map[int64]struct{}
deploymentID string
derpMeshKey string
lastUpdateCheck []byte
serviceBanner []byte
logoURL string
appSecurityKey string
lastLicenseID int32
defaultProxyDisplayName string
defaultProxyIconURL string
}
func validateDatabaseTypeWithValid(v reflect.Value) (handled bool, err error) {
@ -5171,3 +5176,16 @@ func isNull(v interface{}) bool {
func isNotNull(v interface{}) bool {
return reflect.ValueOf(v).FieldByName("Valid").Bool()
}
func (q *fakeQuerier) GetDefaultProxyConfig(_ context.Context) (database.GetDefaultProxyConfigRow, error) {
return database.GetDefaultProxyConfigRow{
DisplayName: q.defaultProxyDisplayName,
IconUrl: q.defaultProxyIconURL,
}, nil
}
func (q *fakeQuerier) UpsertDefaultProxy(_ context.Context, arg database.UpsertDefaultProxyParams) error {
q.defaultProxyDisplayName = arg.DisplayName
q.defaultProxyIconURL = arg.IconUrl
return nil
}

View File

@ -1518,3 +1518,17 @@ func (m metricsStore) GetAuthorizedUserCount(ctx context.Context, arg database.G
m.queryLatencies.WithLabelValues("GetAuthorizedUserCount").Observe(time.Since(start).Seconds())
return count, err
}
func (m metricsStore) UpsertDefaultProxy(ctx context.Context, arg database.UpsertDefaultProxyParams) error {
start := time.Now()
err := m.s.UpsertDefaultProxy(ctx, arg)
m.queryLatencies.WithLabelValues("UpsertDefaultProxy").Observe(time.Since(start).Seconds())
return err
}
func (m metricsStore) GetDefaultProxyConfig(ctx context.Context) (database.GetDefaultProxyConfigRow, error) {
start := time.Now()
resp, err := m.s.GetDefaultProxyConfig(ctx)
m.queryLatencies.WithLabelValues("GetDefaultProxyConfig").Observe(time.Since(start).Seconds())
return resp, err
}

View File

@ -418,6 +418,21 @@ func (mr *MockStoreMockRecorder) GetDERPMeshKey(arg0 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDERPMeshKey", reflect.TypeOf((*MockStore)(nil).GetDERPMeshKey), arg0)
}
// GetDefaultProxyConfig mocks base method.
func (m *MockStore) GetDefaultProxyConfig(arg0 context.Context) (database.GetDefaultProxyConfigRow, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetDefaultProxyConfig", arg0)
ret0, _ := ret[0].(database.GetDefaultProxyConfigRow)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetDefaultProxyConfig indicates an expected call of GetDefaultProxyConfig.
func (mr *MockStoreMockRecorder) GetDefaultProxyConfig(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDefaultProxyConfig", reflect.TypeOf((*MockStore)(nil).GetDefaultProxyConfig), arg0)
}
// GetDeploymentDAUs mocks base method.
func (m *MockStore) GetDeploymentDAUs(arg0 context.Context, arg1 int32) ([]database.GetDeploymentDAUsRow, error) {
m.ctrl.T.Helper()
@ -3088,6 +3103,20 @@ func (mr *MockStoreMockRecorder) UpsertAppSecurityKey(arg0, arg1 interface{}) *g
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertAppSecurityKey", reflect.TypeOf((*MockStore)(nil).UpsertAppSecurityKey), arg0, arg1)
}
// UpsertDefaultProxy mocks base method.
func (m *MockStore) UpsertDefaultProxy(arg0 context.Context, arg1 database.UpsertDefaultProxyParams) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpsertDefaultProxy", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// UpsertDefaultProxy indicates an expected call of UpsertDefaultProxy.
func (mr *MockStoreMockRecorder) UpsertDefaultProxy(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertDefaultProxy", reflect.TypeOf((*MockStore)(nil).UpsertDefaultProxy), arg0, arg1)
}
// UpsertLastUpdateCheck mocks base method.
func (m *MockStore) UpsertLastUpdateCheck(arg0 context.Context, arg1 string) error {
m.ctrl.T.Helper()

View File

@ -186,6 +186,10 @@ func (w WorkspaceProxy) RBACObject() rbac.Object {
WithID(w.ID)
}
func (w WorkspaceProxy) IsPrimary() bool {
return w.Name == "primary"
}
func (f File) RBACObject() rbac.Object {
return rbac.ResourceFile.
WithID(f.ID).

View File

@ -54,6 +54,7 @@ type sqlcQuerier interface {
// are included.
GetAuthorizationUserRoles(ctx context.Context, userID uuid.UUID) (GetAuthorizationUserRolesRow, error)
GetDERPMeshKey(ctx context.Context) (string, error)
GetDefaultProxyConfig(ctx context.Context) (GetDefaultProxyConfigRow, error)
GetDeploymentDAUs(ctx context.Context, tzOffset int32) ([]GetDeploymentDAUsRow, error)
GetDeploymentID(ctx context.Context) (string, error)
GetDeploymentWorkspaceAgentStats(ctx context.Context, createdAt time.Time) (GetDeploymentWorkspaceAgentStatsRow, error)
@ -254,6 +255,10 @@ type sqlcQuerier interface {
UpdateWorkspaceTTL(ctx context.Context, arg UpdateWorkspaceTTLParams) error
UpdateWorkspaceTTLToBeWithinTemplateMax(ctx context.Context, arg UpdateWorkspaceTTLToBeWithinTemplateMaxParams) error
UpsertAppSecurityKey(ctx context.Context, value string) error
// The default proxy is implied and not actually stored in the database.
// So we need to store it's configuration here for display purposes.
// The functional values are immutable and controlled implicitly.
UpsertDefaultProxy(ctx context.Context, arg UpsertDefaultProxyParams) error
UpsertLastUpdateCheck(ctx context.Context, value string) error
UpsertLogoURL(ctx context.Context, value string) error
UpsertServiceBanner(ctx context.Context, value string) error

View File

@ -14,6 +14,7 @@ import (
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/coderd/database/dbgen"
"github.com/coder/coder/coderd/database/migrations"
"github.com/coder/coder/testutil"
)
func TestGetDeploymentWorkspaceAgentStats(t *testing.T) {
@ -257,3 +258,57 @@ func TestProxyByHostname(t *testing.T) {
})
}
}
func TestDefaultProxy(t *testing.T) {
t.Parallel()
if testing.Short() {
t.SkipNow()
}
sqlDB := testSQLDB(t)
err := migrations.Up(sqlDB)
require.NoError(t, err)
db := database.New(sqlDB)
ctx := testutil.Context(t, testutil.WaitLong)
depID := uuid.NewString()
err = db.InsertDeploymentID(ctx, depID)
require.NoError(t, err, "insert deployment id")
// Fetch empty proxy values
defProxy, err := db.GetDefaultProxyConfig(ctx)
require.NoError(t, err, "get def proxy")
require.Equal(t, defProxy.DisplayName, "Default")
require.Equal(t, defProxy.IconUrl, "/emojis/1f3e1.png")
// Set the proxy values
args := database.UpsertDefaultProxyParams{
DisplayName: "displayname",
IconUrl: "/icon.png",
}
err = db.UpsertDefaultProxy(ctx, args)
require.NoError(t, err, "insert def proxy")
defProxy, err = db.GetDefaultProxyConfig(ctx)
require.NoError(t, err, "get def proxy")
require.Equal(t, defProxy.DisplayName, args.DisplayName)
require.Equal(t, defProxy.IconUrl, args.IconUrl)
// Upsert values
args = database.UpsertDefaultProxyParams{
DisplayName: "newdisplayname",
IconUrl: "/newicon.png",
}
err = db.UpsertDefaultProxy(ctx, args)
require.NoError(t, err, "upsert def proxy")
defProxy, err = db.GetDefaultProxyConfig(ctx)
require.NoError(t, err, "get def proxy")
require.Equal(t, defProxy.DisplayName, args.DisplayName)
require.Equal(t, defProxy.IconUrl, args.IconUrl)
// Ensure other site configs are the same
found, err := db.GetDeploymentID(ctx)
require.NoError(t, err, "get deployment id")
require.Equal(t, depID, found)
}

View File

@ -3036,6 +3036,24 @@ func (q *sqlQuerier) GetDERPMeshKey(ctx context.Context) (string, error) {
return value, err
}
const getDefaultProxyConfig = `-- name: GetDefaultProxyConfig :one
SELECT
COALESCE((SELECT value FROM site_configs WHERE key = 'default_proxy_display_name'), 'Default') :: text AS display_name,
COALESCE((SELECT value FROM site_configs WHERE key = 'default_proxy_icon_url'), '/emojis/1f3e1.png') :: text AS icon_url
`
type GetDefaultProxyConfigRow struct {
DisplayName string `db:"display_name" json:"display_name"`
IconUrl string `db:"icon_url" json:"icon_url"`
}
func (q *sqlQuerier) GetDefaultProxyConfig(ctx context.Context) (GetDefaultProxyConfigRow, error) {
row := q.db.QueryRowContext(ctx, getDefaultProxyConfig)
var i GetDefaultProxyConfigRow
err := row.Scan(&i.DisplayName, &i.IconUrl)
return i, err
}
const getDeploymentID = `-- name: GetDeploymentID :one
SELECT value FROM site_configs WHERE key = 'deployment_id'
`
@ -3108,6 +3126,29 @@ func (q *sqlQuerier) UpsertAppSecurityKey(ctx context.Context, value string) err
return err
}
const upsertDefaultProxy = `-- name: UpsertDefaultProxy :exec
INSERT INTO site_configs (key, value)
VALUES
('default_proxy_display_name', $1 :: text),
('default_proxy_icon_url', $2 :: text)
ON CONFLICT
(key)
DO UPDATE SET value = EXCLUDED.value WHERE site_configs.key = EXCLUDED.key
`
type UpsertDefaultProxyParams struct {
DisplayName string `db:"display_name" json:"display_name"`
IconUrl string `db:"icon_url" json:"icon_url"`
}
// The default proxy is implied and not actually stored in the database.
// So we need to store it's configuration here for display purposes.
// The functional values are immutable and controlled implicitly.
func (q *sqlQuerier) UpsertDefaultProxy(ctx context.Context, arg UpsertDefaultProxyParams) error {
_, err := q.db.ExecContext(ctx, upsertDefaultProxy, arg.DisplayName, arg.IconUrl)
return err
}
const upsertLastUpdateCheck = `-- name: UpsertLastUpdateCheck :exec
INSERT INTO site_configs (key, value) VALUES ('last_update_check', $1)
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'last_update_check'

View File

@ -1,3 +1,23 @@
-- name: UpsertDefaultProxy :exec
-- The default proxy is implied and not actually stored in the database.
-- So we need to store it's configuration here for display purposes.
-- The functional values are immutable and controlled implicitly.
INSERT INTO site_configs (key, value)
VALUES
('default_proxy_display_name', @display_name :: text),
('default_proxy_icon_url', @icon_url :: text)
ON CONFLICT
(key)
DO UPDATE SET value = EXCLUDED.value WHERE site_configs.key = EXCLUDED.key
;
-- name: GetDefaultProxyConfig :one
SELECT
COALESCE((SELECT value FROM site_configs WHERE key = 'default_proxy_display_name'), 'Default') :: text AS display_name,
COALESCE((SELECT value FROM site_configs WHERE key = 'default_proxy_icon_url'), '/emojis/1f3e1.png') :: text AS icon_url
;
-- name: InsertDeploymentID :exec
INSERT INTO site_configs (key, value) VALUES ('deployment_id', $1);