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

Commit bcb2e16

Browse files
author
Craig Furman
authored
feat(appliance): optionally load pinned releases file (#64441)
**chore(appliance): version list obtained from backend** Instead of calling release registry directly from the frontend. This commit is just preparation for a fallback mechanism for users that do not want the external dependency on release registry. **feat(appliance): optionally load pinned releases file** Instead of calling release registry. This is a fallback mechanism for airgap users. **feat(appliance): respect pinned release versions during self-update**
1 parent ca6e72f commit bcb2e16

File tree

10 files changed

+139
-21
lines changed

10 files changed

+139
-21
lines changed

cmd/appliance/shared/config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ type Config struct {
2222
http httpConfig
2323
namespace string
2424
relregEndpoint string
25+
pinnedReleasesFile string // airgap fallback
2526
applianceVersion string
2627
selfDeploymentName string
2728
noResourceRestrictions string
@@ -50,6 +51,7 @@ func (c *Config) Load() {
5051
c.applianceVersion = c.Get("APPLIANCE_VERSION", version.Version(), "Version tag for the running appliance.")
5152
c.selfDeploymentName = c.Get("APPLIANCE_DEPLOYMENT_NAME", "", "Own deployment name for self-update. Default is to disable self-update.")
5253
c.relregEndpoint = c.Get("RELEASE_REGISTRY_ENDPOINT", releaseregistry.Endpoint, "Release registry endpoint.")
54+
c.pinnedReleasesFile = c.Get("APPLIANCE_PINNED_RELEASES_FILE", "", "Pinned release versions file.")
5355
c.noResourceRestrictions = c.Get("APPLIANCE_NO_RESOURCE_RESTRICTIONS", "false", "Remove all resource requests and limits from deployed resources. Only recommended for local development.")
5456
}
5557

cmd/appliance/shared/shared.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func Start(ctx context.Context, observationCtx *observation.Context, ready servi
5656
return err
5757
}
5858

59-
app, err := appliance.NewAppliance(k8sClient, relregClient, config.applianceVersion, config.namespace, noResourceRestrictions, logger)
59+
app, err := appliance.NewAppliance(k8sClient, relregClient, config.pinnedReleasesFile, config.applianceVersion, config.namespace, noResourceRestrictions, logger)
6060
if err != nil {
6161
logger.Error("failed to create appliance", log.Error(err))
6262
return err
@@ -108,12 +108,13 @@ func Start(ctx context.Context, observationCtx *observation.Context, ready servi
108108
grpcServer := makeGRPCServer(logger, app)
109109

110110
selfUpdater := &selfupdate.SelfUpdate{
111-
Interval: time.Hour,
112-
Logger: logger.Scoped("SelfUpdate"),
113-
K8sClient: k8sClient,
114-
RelregClient: relregClient,
115-
DeploymentNames: config.selfDeploymentName,
116-
Namespace: config.namespace,
111+
Interval: time.Hour,
112+
Logger: logger.Scoped("SelfUpdate"),
113+
K8sClient: k8sClient,
114+
RelregClient: relregClient,
115+
PinnedReleasesFile: config.pinnedReleasesFile,
116+
DeploymentNames: config.selfDeploymentName,
117+
Namespace: config.namespace,
117118
}
118119

119120
probe := &healthchecker.PodProbe{K8sClient: k8sClient}

internal/appliance/appliance.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ type Appliance struct {
2929
status config.Status
3030
sourcegraph *config.Sourcegraph
3131
releaseRegistryClient *releaseregistry.Client
32+
pinnedReleasesFile string
3233
latestSupportedVersion string
3334
noResourceRestrictions bool
3435
logger log.Logger
@@ -50,6 +51,7 @@ const (
5051
func NewAppliance(
5152
client client.Client,
5253
relregClient *releaseregistry.Client,
54+
pinnedReleasesFile string,
5355
latestSupportedVersion string,
5456
namespace string,
5557
noResourceRestrictions bool,
@@ -58,6 +60,7 @@ func NewAppliance(
5860
app := &Appliance{
5961
client: client,
6062
releaseRegistryClient: relregClient,
63+
pinnedReleasesFile: pinnedReleasesFile,
6164
latestSupportedVersion: latestSupportedVersion,
6265
namespace: namespace,
6366
status: config.StatusInstall,

internal/appliance/frontend/maintenance/src/Install.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
Tabs,
2222
} from '@mui/material'
2323

24+
import { call } from './api'
2425
import { changeStage } from './state'
2526

2627
export const Install: React.FC = () => {
@@ -43,13 +44,7 @@ export const Install: React.FC = () => {
4344
useEffect(() => {
4445
const fetchVersions = async () => {
4546
try {
46-
const response = await fetch('https://releaseregistry.sourcegraph.com/v1/releases/sourcegraph', {
47-
headers: {
48-
Authorization: `Bearer token`,
49-
'Content-Type': 'application/json',
50-
},
51-
mode: 'cors',
52-
})
47+
const response = await call('/api/v1/releases/sourcegraph')
5348
const data = await response.json()
5449
setVersions(data)
5550
if (data.length > 0) {

internal/appliance/json.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"io"
77
"net/http"
8+
"os"
89
"strings"
910

1011
corev1 "k8s.io/api/core/v1"
@@ -167,6 +168,37 @@ func (a *Appliance) getMaintenanceStatusHandler() http.Handler {
167168
})
168169
}
169170

171+
func (a *Appliance) getReleasesHandler() http.Handler {
172+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
173+
var relregResp io.ReadCloser
174+
if a.pinnedReleasesFile == "" {
175+
// simple proxy to release releaseRegistry
176+
resp, err := http.Get("https://releaseregistry.sourcegraph.com/v1/releases/sourcegraph")
177+
if err != nil {
178+
a.serverErrorResponse(w, r, err)
179+
return
180+
}
181+
defer resp.Body.Close()
182+
relregResp = resp.Body
183+
} else {
184+
// airgap fallback
185+
var err error
186+
relregResp, err = os.Open(a.pinnedReleasesFile)
187+
if err != nil {
188+
a.serverErrorResponse(w, r, err)
189+
return
190+
}
191+
defer relregResp.Close()
192+
}
193+
194+
if _, err := io.Copy(w, relregResp); err != nil {
195+
// There's nothing else we can do, we've already sent the status
196+
// code
197+
a.logger.Error("error proxying release registry to appliance frontend", log.Error(err))
198+
}
199+
})
200+
}
201+
170202
func (a *Appliance) postStatusJSONHandler() http.Handler {
171203
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
172204
var input struct {

internal/appliance/routes.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ func (a *Appliance) Routes() *mux.Router {
1919
r.Handle("/api/v1/appliance/status", a.postStatusJSONHandler()).Methods("POST")
2020
r.Handle("/api/v1/appliance/install/progress", a.getInstallProgressJSONHandler()).Methods("GET")
2121
r.Handle("/api/v1/appliance/maintenance/serviceStatuses", a.getMaintenanceStatusHandler()).Methods("GET")
22+
r.Handle("/api/v1/releases/sourcegraph", a.getReleasesHandler()).Methods("GET")
2223

2324
return r
2425
}

internal/appliance/selfupdate/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ go_library(
2121
go_test(
2222
name = "selfupdate_test",
2323
srcs = ["selfupdate_test.go"],
24+
data = glob(["testdata/**"]),
2425
embed = [":selfupdate"],
2526
deps = [
2627
"//internal/releaseregistry",

internal/appliance/selfupdate/selfupdate.go

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package selfupdate
22

33
import (
44
"context"
5+
"encoding/json"
6+
"os"
57
"strings"
68
"time"
79

@@ -18,12 +20,13 @@ import (
1820
)
1921

2022
type SelfUpdate struct {
21-
Interval time.Duration
22-
Logger log.Logger
23-
K8sClient client.Client
24-
RelregClient releaseregistry.ReleaseRegistryClient
25-
DeploymentNames string
26-
Namespace string
23+
Interval time.Duration
24+
Logger log.Logger
25+
K8sClient client.Client
26+
RelregClient releaseregistry.ReleaseRegistryClient
27+
PinnedReleasesFile string
28+
DeploymentNames string
29+
Namespace string
2730
}
2831

2932
func (u *SelfUpdate) Loop(ctx context.Context) error {
@@ -80,7 +83,7 @@ func (u *SelfUpdate) Once(ctx context.Context) error {
8083
}
8184

8285
func (u *SelfUpdate) getLatestTag(ctx context.Context) (string, error) {
83-
versions, err := u.RelregClient.ListVersions(ctx, "sourcegraph")
86+
versions, err := u.getVersions(ctx)
8487
if err != nil {
8588
return "", err
8689
}
@@ -100,6 +103,22 @@ func (u *SelfUpdate) getLatestTag(ctx context.Context) (string, error) {
100103
return latestVersion, nil
101104
}
102105

106+
func (u *SelfUpdate) getVersions(ctx context.Context) ([]releaseregistry.ReleaseInfo, error) {
107+
if u.PinnedReleasesFile != "" {
108+
file, err := os.Open(u.PinnedReleasesFile)
109+
if err != nil {
110+
return nil, errors.Wrap(err, "opening pinned releases file")
111+
}
112+
defer file.Close()
113+
var versions []releaseregistry.ReleaseInfo
114+
if err := json.NewDecoder(file).Decode(&versions); err != nil {
115+
return nil, err
116+
}
117+
return versions, nil
118+
}
119+
return u.RelregClient.ListVersions(ctx, "sourcegraph")
120+
}
121+
103122
// I thought about using regular expressions for this but I swear that's not
104123
// better.
105124
func replaceTag(image, newTag string) string {

internal/appliance/selfupdate/selfupdate_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package selfupdate
22

33
import (
44
"context"
5+
"path/filepath"
56
"testing"
67

78
"github.com/stretchr/testify/require"
@@ -42,3 +43,14 @@ func TestGetLatestTag_ReturnsLatestSupportedPublicVersion(t *testing.T) {
4243
require.NoError(t, err)
4344
require.Equal(t, "4.5.5", latest)
4445
}
46+
47+
func TestGetLatestTag_ReturnsLatestSupportedPublicVersion_FromFileWhenSpecified(t *testing.T) {
48+
selfUpdater := &SelfUpdate{
49+
Logger: logtest.Scoped(t),
50+
PinnedReleasesFile: filepath.Join("testdata", "releases.json"),
51+
}
52+
53+
latest, err := selfUpdater.getLatestTag(context.Background())
54+
require.NoError(t, err)
55+
require.Equal(t, "5.6.185", latest)
56+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
[
2+
{
3+
"id": 117,
4+
"name": "sourcegraph",
5+
"public": false,
6+
"created_at": "2024-08-12T23:37:41.351739Z",
7+
"promoted_at": null,
8+
"version": "v5.6.877",
9+
"git_sha": "6e423717731ee5e672851353ef4163b92da0034a",
10+
"is_development": false
11+
},
12+
{
13+
"id": 112,
14+
"name": "sourcegraph",
15+
"public": true,
16+
"created_at": "2024-08-08T21:22:13.186324Z",
17+
"promoted_at": "2024-08-08T22:07:23.412792Z",
18+
"version": "v5.6.185",
19+
"git_sha": "02a08981d047b648f903dc4f707a15e5a79a6e3f",
20+
"is_development": false
21+
},
22+
{
23+
"id": 107,
24+
"name": "sourcegraph",
25+
"public": true,
26+
"created_at": "2024-08-07T19:41:31.970668Z",
27+
"promoted_at": "2024-08-07T22:53:00.840214Z",
28+
"version": "v5.6.0",
29+
"git_sha": "0fd6235fa41fdc9dbfac748e232e4a3a823acf66",
30+
"is_development": false
31+
},
32+
{
33+
"id": 106,
34+
"name": "sourcegraph",
35+
"public": false,
36+
"created_at": "2024-08-06T05:42:27.548894Z",
37+
"promoted_at": null,
38+
"version": "v5.5.4710",
39+
"git_sha": "a310035006c9738406ceb9beda916c843e37edd4",
40+
"is_development": false
41+
},
42+
{
43+
"id": 101,
44+
"name": "sourcegraph",
45+
"public": true,
46+
"created_at": "2024-08-02T00:30:06.845858Z",
47+
"promoted_at": "2024-08-02T01:34:59.987836Z",
48+
"version": "v5.5.3956",
49+
"git_sha": "dc97541a285b15f50998ef176fc1d882b3f42424",
50+
"is_development": false
51+
}
52+
]

0 commit comments

Comments
 (0)