Skip to content
This repository was archived by the owner on Sep 30, 2024. It is now read-only.

Commit b47c376

Browse files
author
Craig Furman
authored
fix(appliance): source versions from release registry (#63387)
Rather than hardcoding a few. Present the user with versions up to 2 minor revisions back from the version of the appliance itself, which should be in lock-step with the rest of the monorepo. Closes https://linear.app/sourcegraph/issue/REL-199/populate-accurate-list-of-versions-to-install
1 parent 43926a7 commit b47c376

File tree

14 files changed

+315
-30
lines changed

14 files changed

+315
-30
lines changed

cmd/appliance/README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,34 @@ Appliance runs as a standard Kubernetes Deployment and utilizes Kubernetes [cont
1111
## Own
1212

1313
For more information or for help, see the [Release Team](https://handbook.sourcegraph.com/departments/engineering/teams/release/).
14+
15+
## Development
16+
17+
You can kick the tires on the appliance version by running:
18+
19+
```
20+
go run ./cmd/appliance
21+
```
22+
23+
[`config.go`](./shared/config.go) is the source of truth on appliance
24+
configuration. Most of the variables there are optional, except for:
25+
26+
- `APPLIANCE_VERSION`: while this does have a default that does not need to be
27+
overridden in production, development builds that lack the link-time injected
28+
version information will need to set this. Set it to the latest version of
29+
Sourcegraph that you want to be offered.
30+
31+
You might want to override the listen addresses to localhost-only, in order to
32+
avoid macos firewall popups.
33+
34+
The appliance doesn't care if it's running inside or out of the k8s cluster it's
35+
provisioning resources into. It does a well-known k8s config dance to try to
36+
load in-cluster config (from a k8s ServiceAccount token), falling back on
37+
looking for a kubeconfig on the host
38+
39+
If you have some kubernetes running (e.g. minikube, docker desktop), and your
40+
default context is set in ~/.kube/config, the appliance will build a k8s client
41+
using that kubeconfig, and everything should "just work".
42+
43+
See [`development.md`](../..internal/appliance/development.md) for more
44+
information, including about automated testing.

cmd/appliance/shared/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ go_library(
1717
"//internal/env",
1818
"//internal/grpc/defaults",
1919
"//internal/observation",
20+
"//internal/releaseregistry",
2021
"//internal/service",
22+
"//internal/version",
2123
"//lib/errors",
2224
"@com_github_sourcegraph_log//:log",
2325
"@com_github_sourcegraph_log_logr//:logr",

cmd/appliance/shared/config.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,21 @@ import (
88
"k8s.io/client-go/util/homedir"
99

1010
"github.com/sourcegraph/sourcegraph/internal/env"
11+
"github.com/sourcegraph/sourcegraph/internal/releaseregistry"
12+
"github.com/sourcegraph/sourcegraph/internal/version"
1113
"github.com/sourcegraph/sourcegraph/lib/errors"
1214
)
1315

1416
type Config struct {
1517
env.BaseConfig
1618

17-
k8sConfig *rest.Config
18-
metrics metricsConfig
19-
grpc grpcConfig
20-
http httpConfig
21-
namespace string
19+
k8sConfig *rest.Config
20+
metrics metricsConfig
21+
grpc grpcConfig
22+
http httpConfig
23+
namespace string
24+
relregEndpoint string
25+
applianceVersion string
2226
}
2327

2428
func (c *Config) Load() {
@@ -41,6 +45,8 @@ func (c *Config) Load() {
4145
c.grpc.addr = c.Get("APPLIANCE_GRPC_ADDR", ":9000", "Appliance gRPC address.")
4246
c.http.addr = c.Get("APPLIANCE_HTTP_ADDR", ":8080", "Appliance http address.")
4347
c.namespace = c.Get("APPLIANCE_NAMESPACE", "default", "Namespace to monitor.")
48+
c.applianceVersion = c.Get("APPLIANCE_VERSION", version.Version(), "Version tag for the running appliance.")
49+
c.relregEndpoint = c.Get("RELEASE_REGISTRY_ENDPOINT", releaseregistry.Endpoint, "Release registry endpoint.")
4450
}
4551

4652
func (c *Config) Validate() error {

cmd/appliance/shared/shared.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
pb "github.com/sourcegraph/sourcegraph/internal/appliance/v1"
2525
"github.com/sourcegraph/sourcegraph/internal/grpc/defaults"
2626
"github.com/sourcegraph/sourcegraph/internal/observation"
27+
"github.com/sourcegraph/sourcegraph/internal/releaseregistry"
2728
"github.com/sourcegraph/sourcegraph/internal/service"
2829
"github.com/sourcegraph/sourcegraph/lib/errors"
2930
)
@@ -42,7 +43,9 @@ func Start(ctx context.Context, observationCtx *observation.Context, ready servi
4243
return err
4344
}
4445

45-
app := appliance.NewAppliance(k8sClient, config.namespace, logger)
46+
relregClient := releaseregistry.NewClient(config.relregEndpoint)
47+
48+
app := appliance.NewAppliance(k8sClient, relregClient, config.applianceVersion, config.namespace, logger)
4649

4750
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
4851
Logger: logr,

dev/sg/internal/release/list_release.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func listRegistryVersions(cmd *cli.Context) error {
3333

3434
out := std.NewOutput(os.Stderr, false)
3535
pending := out.Pending(output.Line("", output.StylePending, "Fetching versions from the release registry..."))
36-
versions, err := client.ListVersions(cmd.Context)
36+
versions, err := client.ListVersions(cmd.Context, "")
3737
if err != nil {
3838
pending.Complete(output.Linef(output.EmojiFailure, output.StyleFailure, "Failed to fetch versions from release registry"))
3939
return err

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ require (
257257
github.com/Azure/azure-sdk-for-go/sdk/ai/azopenai v0.5.0
258258
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.2
259259
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0
260+
github.com/Masterminds/semver/v3 v3.2.1
260261
github.com/aws/constructs-go/constructs/v10 v10.3.0
261262
github.com/aws/jsii-runtime-go v1.98.0
262263
github.com/bazelbuild/bazel-gazelle v0.35.0
@@ -340,7 +341,6 @@ require (
340341
github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 // indirect
341342
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.21.0 // indirect
342343
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.45.0 // indirect
343-
github.com/Masterminds/semver/v3 v3.2.1 // indirect
344344
github.com/Masterminds/squirrel v1.5.4 // indirect
345345
github.com/PuerkitoBio/goquery v1.8.1 // indirect
346346
github.com/agnivade/levenshtein v1.1.1 // indirect

internal/appliance/BUILD.bazel

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
load("//dev:go_defs.bzl", "go_test")
12
load("@io_bazel_rules_go//go:def.bzl", "go_library")
23

34
go_library(
@@ -8,6 +9,7 @@ go_library(
89
"grpc.go",
910
"html.go",
1011
"routes.go",
12+
"versions.go",
1113
],
1214
embedsrcs = [
1315
"web/static/img/favicon.png",
@@ -22,8 +24,12 @@ go_library(
2224
deps = [
2325
"//internal/appliance/config",
2426
"//internal/appliance/v1:appliance",
27+
"//internal/releaseregistry",
28+
"//lib/errors",
2529
"//lib/pointers",
2630
"@com_github_gorilla_mux//:mux",
31+
"@com_github_life4_genesis//slices",
32+
"@com_github_masterminds_semver_v3//:semver",
2733
"@com_github_sourcegraph_log//:log",
2834
"@io_k8s_api//core/v1:core",
2935
"@io_k8s_apimachinery//pkg/api/errors",
@@ -38,3 +44,12 @@ filegroup(
3844
name = "testdata",
3945
srcs = glob(["testdata/**"]),
4046
)
47+
48+
go_test(
49+
name = "appliance_test",
50+
srcs = ["versions_test.go"],
51+
deps = [
52+
":appliance",
53+
"@com_github_stretchr_testify//require",
54+
],
55+
)

internal/appliance/appliance.go

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,18 @@ import (
1313
"github.com/sourcegraph/log"
1414
"github.com/sourcegraph/sourcegraph/internal/appliance/config"
1515
pb "github.com/sourcegraph/sourcegraph/internal/appliance/v1"
16+
"github.com/sourcegraph/sourcegraph/internal/releaseregistry"
1617
"github.com/sourcegraph/sourcegraph/lib/pointers"
1718
)
1819

1920
type Appliance struct {
20-
client client.Client
21-
namespace string
22-
status Status
23-
sourcegraph config.Sourcegraph
24-
logger log.Logger
21+
client client.Client
22+
namespace string
23+
status Status
24+
sourcegraph config.Sourcegraph
25+
releaseRegistryClient *releaseregistry.Client
26+
latestSupportedVersion string
27+
logger log.Logger
2528

2629
// Embed the UnimplementedApplianceServiceServer structs to ensure forwards compatibility (if the service is
2730
// compiled against a newer version of the proto file, the server will still have default implementations of any new
@@ -42,13 +45,21 @@ func (s Status) String() string {
4245
return string(s)
4346
}
4447

45-
func NewAppliance(client client.Client, namespace string, logger log.Logger) *Appliance {
48+
func NewAppliance(
49+
client client.Client,
50+
relregClient *releaseregistry.Client,
51+
latestSupportedVersion string,
52+
namespace string,
53+
logger log.Logger,
54+
) *Appliance {
4655
return &Appliance{
47-
client: client,
48-
namespace: namespace,
49-
status: StatusSetup,
50-
sourcegraph: config.Sourcegraph{},
51-
logger: logger,
56+
client: client,
57+
releaseRegistryClient: relregClient,
58+
latestSupportedVersion: latestSupportedVersion,
59+
namespace: namespace,
60+
status: StatusSetup,
61+
sourcegraph: config.Sourcegraph{},
62+
logger: logger,
5263
}
5364
}
5465

internal/appliance/html.go

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@ package appliance
22

33
import (
44
"context"
5+
"fmt"
56
"html/template"
67
"net/http"
78

9+
"github.com/life4/genesis/slices"
10+
811
"github.com/sourcegraph/log"
912

1013
"github.com/sourcegraph/sourcegraph/internal/appliance/config"
14+
"github.com/sourcegraph/sourcegraph/internal/releaseregistry"
1115
)
1216

1317
const (
@@ -29,11 +33,47 @@ func (a *Appliance) applianceHandler(w http.ResponseWriter, r *http.Request) {
2933
}
3034

3135
func (a *Appliance) getSetupHandler(w http.ResponseWriter, r *http.Request) {
32-
err := setupTmpl.Execute(w, "")
36+
versions, err := a.getVersions(r.Context())
3337
if err != nil {
34-
a.logger.Error("failed to execute templating", log.Error(err))
35-
// Handle err
38+
a.handleError(w, err, "getting versions")
39+
return
40+
}
41+
versions, err = NMinorVersions(versions, a.latestSupportedVersion, 2)
42+
if err != nil {
43+
a.handleError(w, err, "filtering versions to 2 minor points")
44+
return
45+
}
46+
47+
err = setupTmpl.Execute(w, struct {
48+
Versions []string
49+
}{
50+
Versions: versions,
51+
})
52+
if err != nil {
53+
a.handleError(w, err, "executing template")
54+
return
55+
}
56+
}
57+
58+
func (a *Appliance) handleError(w http.ResponseWriter, err error, msg string) {
59+
a.logger.Error(msg, log.Error(err))
60+
61+
// TODO we should probably look twice at this and decide whether it's in
62+
// line with existing standards.
63+
// Don't leak details of internal errors to users - that's why we have
64+
// logging above.
65+
w.WriteHeader(http.StatusInternalServerError)
66+
fmt.Fprintln(w, "Something went wrong - please contact support.")
67+
}
68+
69+
func (a *Appliance) getVersions(ctx context.Context) ([]string, error) {
70+
versions, err := a.releaseRegistryClient.ListVersions(ctx, "sourcegraph")
71+
if err != nil {
72+
return nil, err
3673
}
74+
return slices.MapFilter(versions, func(version releaseregistry.ReleaseInfo) (string, bool) {
75+
return version.Version, version.Public
76+
}), nil
3777
}
3878

3979
func (a *Appliance) postSetupHandler(w http.ResponseWriter, r *http.Request) {

internal/appliance/reconciler/cadvisor.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import (
2020

2121
func (r *Reconciler) reconcileCadvisor(ctx context.Context, sg *config.Sourcegraph, owner client.Object) error {
2222
if err := r.reconcileCadvisorDaemonset(ctx, sg, owner); err != nil {
23-
return errors.Wrap(err, "reconciling ClusterRole")
23+
return errors.Wrap(err, "reconciling Daemonset")
2424
}
2525
if err := r.reconcileCadvisorServiceAccount(ctx, sg, owner); err != nil {
2626
return errors.Wrap(err, "reconciling ServiceAccount")

0 commit comments

Comments
 (0)