Skip to content

Commit 983b483

Browse files
akosyakovroboquat
authored andcommitted
[jb] sync initial options
1 parent eade024 commit 983b483

File tree

2 files changed

+163
-54
lines changed

2 files changed

+163
-54
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/bin/bash
2+
# Copyright (c) 2022 Gitpod GmbH. All rights reserved.
3+
# Licensed under the GNU Affero General Public License (AGPL).
4+
# See License-AGPL.txt in the project root for license information.
5+
6+
set -Eeuo pipefail
7+
8+
# Login in GCloud to reuse remote caches
9+
ROOT_DIR="$(dirname "$0")/../../../.."
10+
11+
source "$ROOT_DIR/dev/preview/workflow/lib/ensure-gcloud-auth.sh"
12+
ensure_gcloud_auth
13+
14+
# This script builds the backend JB ide image and updates the IDE config map.
15+
16+
product=${1:-intellij}
17+
echo "Product: $product"
18+
19+
qualifier=${2:-latest}
20+
echo "Qualifier: $qualifier"
21+
22+
if [ "$qualifier" == "stable" ]; then
23+
component=$product
24+
else
25+
component=$product-$qualifier
26+
fi
27+
28+
version="dev-$component-image-$(date +%F_T"%H-%M-%S")"
29+
echo "Image Version: $version"
30+
31+
bldfn="/tmp/build-$version.tar.gz"
32+
33+
docker ps &> /dev/null || (echo "You need a working Docker daemon. Maybe set DOCKER_HOST?"; exit 1)
34+
leeway build -Dversion="$version" -DimageRepoBase=eu.gcr.io/gitpod-core-dev/build ".:$component" --save "$bldfn" --dont-retag
35+
dev_image="$(tar xfO "$bldfn" ./imgnames.txt | head -n1)"
36+
echo "Dev Image: $dev_image"
37+
38+
if [ "$qualifier" == "stable" ]; then
39+
prop="image"
40+
else
41+
prop="latestImage"
42+
fi
43+
44+
cf_patch=$(kubectl get cm ide-config -o=json | jq '.data."config.json"' |jq -r)
45+
cf_patch=$(echo "$cf_patch" |jq ".ideOptions.options.$product.$prop = \"$dev_image\"")
46+
cf_patch=$(echo "$cf_patch" |jq tostring)
47+
cf_patch="{\"data\": {\"config.json\": $cf_patch}}"
48+
49+
kubectl patch cm ide-config --type=merge -p "$cf_patch"
50+
51+
kubectl rollout restart deployment ide-service

components/ide/jetbrains/image/status/main.go

Lines changed: 112 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,10 @@
55
package main
66

77
import (
8-
"bytes"
98
"context"
109
"encoding/json"
1110
"errors"
1211
"fmt"
13-
"io"
1412
"io/ioutil"
1513
"net"
1614
"net/http"
@@ -20,7 +18,6 @@ import (
2018
"os/signal"
2119
"path/filepath"
2220
"reflect"
23-
"regexp"
2421
"strconv"
2522
"strings"
2623
"syscall"
@@ -50,9 +47,19 @@ var (
5047
const BackendPath = "/ide-desktop/backend"
5148
const ProductInfoPath = BackendPath + "/product-info.json"
5249

50+
type LaunchContext struct {
51+
alias string
52+
projectDir string
53+
configDir string
54+
systemDir string
55+
projectConfigDir string
56+
wsInfo *supervisor.WorkspaceInfoResponse
57+
}
58+
5359
// JB startup entrypoint
5460
func main() {
5561
log.Init(ServiceName, Version, true, false)
62+
log.Info(ServiceName + ": " + Version)
5663
startTime := time.Now()
5764

5865
if len(os.Args) < 3 {
@@ -65,7 +72,13 @@ func main() {
6572
label = os.Args[3]
6673
}
6774

68-
backendVersion, err := resolveBackendVersion()
75+
info, err := resolveProductInfo()
76+
if err != nil {
77+
log.WithError(err).Error("failed to resolve product info")
78+
return
79+
}
80+
81+
backendVersion, err := version.NewVersion(info.Version)
6982
if err != nil {
7083
log.WithError(err).Error("failed to resolve backend version")
7184
return
@@ -77,8 +90,8 @@ func main() {
7790
return
7891
}
7992

80-
repoRoot := wsInfo.GetCheckoutLocation()
81-
gitpodConfig, err := parseGitpodConfig(repoRoot)
93+
projectDir := wsInfo.GetCheckoutLocation()
94+
gitpodConfig, err := parseGitpodConfig(projectDir)
8295
if err != nil {
8396
log.WithError(err).Error("failed to parse .gitpod.yml")
8497
}
@@ -95,10 +108,32 @@ func main() {
95108
log.WithError(err).Error("failed to configure vmoptions")
96109
}
97110

111+
qualifier := os.Getenv("JETBRAINS_BACKEND_QUALIFIER")
112+
if qualifier == "stable" {
113+
qualifier = ""
114+
} else {
115+
qualifier = "-" + qualifier
116+
}
117+
configDir := fmt.Sprintf("/workspace/.config/JetBrains%s", qualifier)
118+
launchCtx := &LaunchContext{
119+
alias: alias,
120+
wsInfo: wsInfo,
121+
projectDir: projectDir,
122+
configDir: configDir,
123+
systemDir: fmt.Sprintf("/workspace/.cache/JetBrains%s", qualifier),
124+
projectConfigDir: fmt.Sprintf("%s/RemoteDev-%s/%s", configDir, info.ProductCode, strings.ReplaceAll(projectDir, "/", "_")),
125+
}
126+
127+
// sync initial options
128+
err = syncOptions(launchCtx)
129+
if err != nil {
130+
log.WithError(err).Error("failed to sync initial options")
131+
}
132+
98133
// install project plugins
99134
version_2022_1, _ := version.NewVersion("2022.1")
100135
if version_2022_1.LessThanOrEqual(backendVersion) {
101-
err = installPlugins(repoRoot, gitpodConfig, alias)
136+
err = installPlugins(gitpodConfig, launchCtx)
102137
installPluginsCost := time.Now().Local().Sub(startTime).Milliseconds()
103138
if err != nil {
104139
log.WithError(err).WithField("cost", installPluginsCost).Error("installing repo plugins: done")
@@ -112,7 +147,7 @@ func main() {
112147
if err != nil {
113148
log.WithError(err).Error("failed to install gitpod-remote plugin")
114149
}
115-
go run(wsInfo, alias)
150+
go run(launchCtx)
116151

117152
debugAgentPrefix := "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:"
118153
http.HandleFunc("/debug", func(w http.ResponseWriter, r *http.Request) {
@@ -324,13 +359,13 @@ func resolveWorkspaceInfo(ctx context.Context) (*supervisor.WorkspaceInfoRespons
324359
return nil, errors.New("failed with attempt 10 times")
325360
}
326361

327-
func run(wsInfo *supervisor.WorkspaceInfoResponse, alias string) {
362+
func run(launchCtx *LaunchContext) {
328363
var args []string
329364
args = append(args, "run")
330-
args = append(args, wsInfo.GetCheckoutLocation())
331-
cmd := remoteDevServerCmd(args)
332-
cmd.Env = append(cmd.Env, "JETBRAINS_GITPOD_BACKEND_KIND="+alias)
333-
workspaceUrl, err := url.Parse(wsInfo.WorkspaceUrl)
365+
args = append(args, launchCtx.projectDir)
366+
cmd := remoteDevServerCmd(args, launchCtx)
367+
cmd.Env = append(cmd.Env, "JETBRAINS_GITPOD_BACKEND_KIND="+launchCtx.alias)
368+
workspaceUrl, err := url.Parse(launchCtx.wsInfo.WorkspaceUrl)
334369
if err == nil {
335370
cmd.Env = append(cmd.Env, "JETBRAINS_GITPOD_WORKSPACE_HOST="+workspaceUrl.Hostname())
336371
}
@@ -342,7 +377,7 @@ func run(wsInfo *supervisor.WorkspaceInfoResponse, alias string) {
342377
}
343378

344379
// Nicely handle SIGTERM sinal
345-
go handleSignal(wsInfo.GetCheckoutLocation())
380+
go handleSignal()
346381

347382
if err := cmd.Wait(); err != nil {
348383
log.WithError(err).Error("failed to wait")
@@ -351,28 +386,22 @@ func run(wsInfo *supervisor.WorkspaceInfoResponse, alias string) {
351386
os.Exit(cmd.ProcessState.ExitCode())
352387
}
353388

354-
func remoteDevServerCmd(args []string) *exec.Cmd {
389+
func remoteDevServerCmd(args []string, productContext *LaunchContext) *exec.Cmd {
355390
cmd := exec.Command(BackendPath+"/bin/remote-dev-server.sh", args...)
356391
cmd.Env = os.Environ()
357392

358393
// Set default config and system directories under /workspace to preserve between restarts
359-
qualifier := os.Getenv("JETBRAINS_BACKEND_QUALIFIER")
360-
if qualifier == "stable" {
361-
qualifier = ""
362-
} else {
363-
qualifier = "-" + qualifier
364-
}
365394
cmd.Env = append(cmd.Env,
366-
fmt.Sprintf("IJ_HOST_CONFIG_BASE_DIR=/workspace/.config/JetBrains%s", qualifier),
367-
fmt.Sprintf("IJ_HOST_SYSTEM_BASE_DIR=/workspace/.cache/JetBrains%s", qualifier),
395+
fmt.Sprintf("IJ_HOST_CONFIG_BASE_DIR=%s", productContext.configDir),
396+
fmt.Sprintf("IJ_HOST_SYSTEM_BASE_DIR=%s", productContext.systemDir),
368397
)
369398

370399
cmd.Stderr = os.Stderr
371400
cmd.Stdout = os.Stdout
372401
return cmd
373402
}
374403

375-
func handleSignal(projectPath string) {
404+
func handleSignal() {
376405
sigChan := make(chan os.Signal, 1)
377406
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
378407

@@ -488,10 +517,11 @@ func updateVMOptions(
488517
}
489518
*/
490519
type ProductInfo struct {
491-
Version string `json:"version"`
520+
Version string `json:"version"`
521+
ProductCode string `json:"productCode"`
492522
}
493523

494-
func resolveBackendVersion() (*version.Version, error) {
524+
func resolveProductInfo() (*ProductInfo, error) {
495525
f, err := os.Open(ProductInfoPath)
496526
if err != nil {
497527
return nil, err
@@ -504,52 +534,80 @@ func resolveBackendVersion() (*version.Version, error) {
504534

505535
var info ProductInfo
506536
err = json.Unmarshal(content, &info)
507-
if err != nil {
508-
return nil, err
509-
}
510-
return version.NewVersion(info.Version)
537+
return &info, err
511538
}
512539

513-
func installPlugins(repoRoot string, config *gitpod.GitpodConfig, alias string) error {
514-
plugins, err := getPlugins(config, alias)
540+
func syncOptions(launchCtx *LaunchContext) error {
541+
userHomeDir, err := os.UserHomeDir()
515542
if err != nil {
516543
return err
517544
}
518-
if len(plugins) <= 0 {
545+
var srcDirs []string
546+
for _, srcDir := range []string{
547+
fmt.Sprintf("%s/.gitpod/jetbrains/options", userHomeDir),
548+
fmt.Sprintf("%s/.gitpod/jetbrains/%s/options", userHomeDir, launchCtx.alias),
549+
fmt.Sprintf("%s/.gitpod/jetbrains/options", launchCtx.projectDir),
550+
fmt.Sprintf("%s/.gitpod/jetbrains/%s/options", launchCtx.projectDir, launchCtx.alias),
551+
} {
552+
srcStat, err := os.Stat(srcDir)
553+
if os.IsNotExist(err) {
554+
// nothing to sync
555+
continue
556+
}
557+
if err != nil {
558+
return err
559+
}
560+
if !srcStat.IsDir() {
561+
return fmt.Errorf("%s is not a directory", srcDir)
562+
}
563+
srcDirs = append(srcDirs, srcDir)
564+
}
565+
if len(srcDirs) == 0 {
566+
// nothing to sync
519567
return nil
520568
}
521-
r, w, err := os.Pipe()
569+
570+
destDir := fmt.Sprintf("%s/options", launchCtx.projectConfigDir)
571+
_, err = os.Stat(destDir)
572+
if !os.IsNotExist(err) {
573+
// already synced skipping, i.e. restart of jb backend
574+
return nil
575+
}
576+
err = os.MkdirAll(destDir, os.ModePerm)
522577
if err != nil {
523578
return err
524579
}
525-
defer r.Close()
526580

527-
outC := make(chan string)
528-
go func() {
529-
var buf bytes.Buffer
530-
_, _ = io.Copy(&buf, r)
531-
outC <- buf.String()
532-
}()
581+
for _, srcDir := range srcDirs {
582+
cp := exec.Command("cp", "-rf", srcDir+"/.", destDir)
583+
err = cp.Run()
584+
if err != nil {
585+
return err
586+
}
587+
}
588+
return nil
589+
}
590+
591+
func installPlugins(config *gitpod.GitpodConfig, launchCtx *LaunchContext) error {
592+
plugins, err := getPlugins(config, launchCtx.alias)
593+
if err != nil {
594+
return err
595+
}
596+
if len(plugins) <= 0 {
597+
return nil
598+
}
533599

534600
var args []string
535601
args = append(args, "installPlugins")
536-
args = append(args, repoRoot)
602+
args = append(args, launchCtx.projectDir)
537603
args = append(args, plugins...)
538-
cmd := remoteDevServerCmd(args)
539-
cmd.Stdout = io.MultiWriter(w, os.Stdout)
604+
cmd := remoteDevServerCmd(args, launchCtx)
540605
installErr := cmd.Run()
541606

542607
// delete alien_plugins.txt to suppress 3rd-party plugins consent on startup to workaround backend startup freeze
543-
w.Close()
544-
out := <-outC
545-
configR := regexp.MustCompile("IDE config directory: (\\S+)\n")
546-
matches := configR.FindStringSubmatch(out)
547-
if len(matches) == 2 {
548-
configDir := matches[1]
549-
err := os.Remove(configDir + "/alien_plugins.txt")
550-
if err != nil {
551-
log.WithError(err).Error("failed to suppress 3rd-party plugins consent")
552-
}
608+
err = os.Remove(launchCtx.projectConfigDir + "/alien_plugins.txt")
609+
if err != nil {
610+
log.WithError(err).Error("failed to suppress 3rd-party plugins consent")
553611
}
554612

555613
if installErr != nil {

0 commit comments

Comments
 (0)