2023-02-20 16:10:50 +01:00

109 lines
3.7 KiB
Go

// Copyright (c) 2022 Gitpod GmbH. All rights reserved.
// Licensed under the GNU Affero General Public License (AGPL).
// See License.AGPL.txt in the project root for license information.
package server
import (
"context"
_ "embed"
"encoding/json"
"fmt"
"github.com/gitpod-io/gitpod/common-go/log"
"github.com/gitpod-io/gitpod/ide-service-api/config"
oci_tool "github.com/gitpod-io/gitpod/ide-service/pkg/ocitool"
"github.com/xeipuuv/gojsonschema"
"golang.org/x/xerrors"
)
//go:embed schema.json
var jsonScheme []byte
// ParseConfig parse and validate ide config
func ParseConfig(ctx context.Context, b []byte) (*config.IDEConfig, error) {
var cfg config.IDEConfig
if err := json.Unmarshal(b, &cfg); err != nil {
return nil, xerrors.Errorf("cannot parse ide config: %w", err)
}
// validate with json schema
schemaLoader := gojsonschema.NewBytesLoader(jsonScheme)
documentLoader := gojsonschema.NewBytesLoader(b)
result, err := gojsonschema.Validate(schemaLoader, documentLoader)
if err != nil {
return nil, xerrors.Errorf("invalid: %w", err)
}
if !result.Valid() {
log.Error("invalid ide config")
for _, desc := range result.Errors() {
log.WithField("desc", desc).Error("invalid ide config desc")
}
return nil, xerrors.New("invalid ide config")
}
if err := checkIDEExistsInOptions(cfg, cfg.IdeOptions.DefaultIde, config.IDETypeBrowser, "DefaultIde"); err != nil {
return nil, err
}
if err := checkIDEExistsInOptions(cfg, cfg.IdeOptions.DefaultDesktopIde, config.IDETypeDesktop, "DefaultDesktopIde"); err != nil {
return nil, err
}
for clientId, client := range cfg.IdeOptions.Clients {
if err := checkIDEExistsInOptions(cfg, client.DefaultDesktopIDE, config.IDETypeDesktop, fmt.Sprintf("client %s DefaultDesktopIDE", clientId)); err != nil {
return nil, err
}
for _, ideId := range client.DesktopIDEs {
if err := checkIDEExistsInOptions(cfg, ideId, config.IDETypeDesktop, fmt.Sprintf("client %s DesktopIDEs %s", clientId, ideId)); err != nil {
return nil, err
}
}
}
// resolve image digest
for id, option := range cfg.IdeOptions.Options {
if option.ResolveImageDigest {
if resolved, err := oci_tool.Resolve(ctx, option.Image); err != nil {
log.WithError(err).Error("ide config: cannot resolve image digest")
} else {
log.WithField("ide", id).WithField("image", option.Image).WithField("resolved", resolved).Info("ide config: resolved latest image digest")
option.Image = resolved
}
}
if resolvedVersion, err := oci_tool.ResolveIDEVersion(ctx, option.Image); err != nil {
log.WithError(err).Error("ide config: cannot get version from image")
} else {
option.ImageVersion = resolvedVersion
}
if option.LatestImage != "" {
if resolved, err := oci_tool.Resolve(ctx, option.LatestImage); err != nil {
log.WithError(err).Error("ide config: cannot resolve latest image digest")
} else {
log.WithField("ide", id).WithField("image", option.LatestImage).WithField("resolved", resolved).Info("ide config: resolved latest image digest")
option.LatestImage = resolved
}
if resolvedVersion, err := oci_tool.ResolveIDEVersion(ctx, option.LatestImage); err != nil {
log.WithError(err).Error("ide config: cannot get version from image")
} else {
option.LatestImageVersion = resolvedVersion
}
}
cfg.IdeOptions.Options[id] = option
}
return &cfg, nil
}
func checkIDEExistsInOptions(c config.IDEConfig, ideId string, ideType config.IDEType, name string) error {
ide, ok := c.IdeOptions.Options[ideId]
if !ok {
return xerrors.Errorf("invalid ide config: %s %s is not an entry of ide options", name, ideId)
}
if ide.Type != ideType {
return xerrors.Errorf("invalid ide config: %s should be %s but %s", name, ideType, ide.Type)
}
return nil
}