2022-08-18 10:48:23 +03:00
|
|
|
package main
|
|
|
|
|
|
|
|
// Copyright (c) Microsoft Corporation.
|
|
|
|
// Licensed under the Apache License 2.0.
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2023-07-11 19:30:36 +03:00
|
|
|
"encoding/base64"
|
|
|
|
"encoding/json"
|
2022-08-18 10:48:23 +03:00
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
|
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
|
|
|
|
"github.com/Azure/ARO-RP/pkg/api"
|
|
|
|
"github.com/Azure/ARO-RP/pkg/database"
|
|
|
|
"github.com/Azure/ARO-RP/pkg/env"
|
|
|
|
"github.com/Azure/ARO-RP/pkg/metrics/statsd"
|
2022-08-26 06:09:16 +03:00
|
|
|
"github.com/Azure/ARO-RP/pkg/util/version"
|
2022-08-18 10:48:23 +03:00
|
|
|
)
|
|
|
|
|
2024-01-18 23:20:03 +03:00
|
|
|
// Corresponds to configuration.openShiftVersions in RP-Config
|
|
|
|
type OpenShiftVersions struct {
|
|
|
|
DefaultStream map[string]string
|
|
|
|
InstallStreams map[string]string
|
|
|
|
}
|
|
|
|
|
|
|
|
func getEnvironmentData(envKey string, envData any) error {
|
2023-07-11 21:14:24 +03:00
|
|
|
var err error
|
|
|
|
|
|
|
|
jsonData := []byte(os.Getenv(envKey))
|
|
|
|
|
|
|
|
// For Azure DevOps pipelines, the JSON data is Base64-encoded
|
|
|
|
// since it's embedded in JSON-formatted build artifacts. But
|
|
|
|
// let's not force that on local development mode.
|
|
|
|
if !env.IsLocalDevelopmentMode() {
|
|
|
|
jsonData, err = base64.StdEncoding.DecodeString(string(jsonData))
|
|
|
|
if err != nil {
|
2024-01-18 23:20:03 +03:00
|
|
|
return fmt.Errorf("%s: Failed to decode base64: %w", envKey, err)
|
2023-07-11 21:14:24 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-18 23:20:03 +03:00
|
|
|
if err = json.Unmarshal(jsonData, envData); err != nil {
|
|
|
|
return fmt.Errorf("%s: Failed to parse JSON: %w", envKey, err)
|
2023-07-11 21:14:24 +03:00
|
|
|
}
|
|
|
|
|
2024-01-18 23:20:03 +03:00
|
|
|
return nil
|
2023-07-11 21:14:24 +03:00
|
|
|
}
|
|
|
|
|
2024-01-18 23:20:03 +03:00
|
|
|
func getOpenShiftVersions() (*OpenShiftVersions, error) {
|
|
|
|
const envKey = envOpenShiftVersions
|
|
|
|
var openShiftVersions OpenShiftVersions
|
|
|
|
|
|
|
|
if err := getEnvironmentData(envKey, &openShiftVersions); err != nil {
|
2022-08-25 10:27:17 +03:00
|
|
|
return nil, err
|
2022-08-18 10:48:23 +03:00
|
|
|
}
|
2022-08-25 10:27:17 +03:00
|
|
|
|
2024-01-18 23:20:03 +03:00
|
|
|
// The DefaultStream map must have exactly one entry.
|
|
|
|
numDefaultStreams := len(openShiftVersions.DefaultStream)
|
|
|
|
if numDefaultStreams != 1 {
|
|
|
|
return nil, fmt.Errorf("%s: DefaultStream must have exactly 1 entry, found %d", envKey, numDefaultStreams)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &openShiftVersions, nil
|
|
|
|
}
|
2023-02-22 20:44:48 +03:00
|
|
|
|
2024-01-18 23:20:03 +03:00
|
|
|
func getInstallerImageDigests() (map[string]string, error) {
|
2023-07-11 19:30:36 +03:00
|
|
|
// INSTALLER_IMAGE_DIGESTS is the mapping of a minor version to
|
|
|
|
// the aro-installer wrapper digest. This allows us to utilize
|
|
|
|
// Azure Safe Deployment Practices (SDP) instead of pushing the
|
|
|
|
// version tag and deploying to all regions at once.
|
2024-01-18 23:20:03 +03:00
|
|
|
const envKey = envInstallerImageDigests
|
|
|
|
var installerImageDigests map[string]string
|
|
|
|
|
|
|
|
if err := getEnvironmentData(envKey, &installerImageDigests); err != nil {
|
2023-07-11 21:14:24 +03:00
|
|
|
return nil, err
|
2023-07-11 19:30:36 +03:00
|
|
|
}
|
|
|
|
|
2024-01-18 23:20:03 +03:00
|
|
|
return installerImageDigests, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func appendOpenShiftVersions(ocpVersions []api.OpenShiftVersion, installStreams map[string]string, installerImageName string, installerImageDigests map[string]string, isDefault bool) ([]api.OpenShiftVersion, error) {
|
|
|
|
for fullVersion, openShiftPullspec := range installStreams {
|
|
|
|
openShiftVersion, err := version.ParseVersion(fullVersion)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
fullVersion = openShiftVersion.String() // trimmed of whitespace
|
|
|
|
minorVersion := openShiftVersion.MinorVersion()
|
|
|
|
installerDigest, ok := installerImageDigests[minorVersion]
|
2023-07-03 22:18:22 +03:00
|
|
|
if !ok {
|
2024-01-18 23:20:03 +03:00
|
|
|
return nil, fmt.Errorf("no installer digest for version %s", minorVersion)
|
2023-07-03 22:18:22 +03:00
|
|
|
}
|
2024-01-18 23:20:03 +03:00
|
|
|
installerPullspec := fmt.Sprintf("%s:%s@%s", installerImageName, minorVersion, installerDigest)
|
2023-07-03 22:18:22 +03:00
|
|
|
|
2023-02-22 20:44:48 +03:00
|
|
|
ocpVersions = append(ocpVersions, api.OpenShiftVersion{
|
|
|
|
Properties: api.OpenShiftVersionProperties{
|
2024-01-18 23:20:03 +03:00
|
|
|
Version: fullVersion,
|
|
|
|
OpenShiftPullspec: openShiftPullspec,
|
|
|
|
InstallerPullspec: installerPullspec,
|
2023-02-22 20:44:48 +03:00
|
|
|
Enabled: true,
|
2024-01-18 23:20:03 +03:00
|
|
|
Default: isDefault,
|
2023-02-22 19:40:41 +03:00
|
|
|
},
|
2023-02-22 20:44:48 +03:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return ocpVersions, nil
|
2022-08-25 10:27:17 +03:00
|
|
|
}
|
2022-08-18 10:48:23 +03:00
|
|
|
|
2024-01-18 23:20:03 +03:00
|
|
|
func getLatestOCPVersions(ctx context.Context, log *logrus.Entry) ([]api.OpenShiftVersion, error) {
|
|
|
|
env, err := env.NewCoreForCI(ctx, log)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
dstAcr := os.Getenv("DST_ACR_NAME")
|
|
|
|
acrDomainSuffix := "." + env.Environment().ContainerRegistryDNSSuffix
|
|
|
|
installerImageName := dstAcr + acrDomainSuffix + "/aro-installer"
|
|
|
|
|
|
|
|
openShiftVersions, err := getOpenShiftVersions()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
installerImageDigests, err := getInstallerImageDigests()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
ocpVersions := make([]api.OpenShiftVersion, 0, len(openShiftVersions.DefaultStream)+len(openShiftVersions.InstallStreams))
|
|
|
|
|
|
|
|
ocpVersions, err = appendOpenShiftVersions(ocpVersions, openShiftVersions.DefaultStream, installerImageName, installerImageDigests, true)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
ocpVersions, err = appendOpenShiftVersions(ocpVersions, openShiftVersions.InstallStreams, installerImageName, installerImageDigests, false)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return ocpVersions, nil
|
|
|
|
}
|
|
|
|
|
2022-08-25 10:27:17 +03:00
|
|
|
func getVersionsDatabase(ctx context.Context, log *logrus.Entry) (database.OpenShiftVersions, error) {
|
2024-01-17 07:17:14 +03:00
|
|
|
_env, err := env.NewCore(ctx, log, env.COMPONENT_UPDATE_OCP_VERSIONS)
|
2022-08-18 10:48:23 +03:00
|
|
|
if err != nil {
|
2022-08-25 10:27:17 +03:00
|
|
|
return nil, err
|
2022-08-18 10:48:23 +03:00
|
|
|
}
|
|
|
|
|
2023-06-01 15:10:36 +03:00
|
|
|
if err = env.ValidateVars("DST_ACR_NAME"); err != nil {
|
|
|
|
return nil, err
|
2022-08-26 05:01:09 +03:00
|
|
|
}
|
|
|
|
|
2022-08-18 10:48:23 +03:00
|
|
|
if !_env.IsLocalDevelopmentMode() {
|
2023-06-01 15:10:36 +03:00
|
|
|
if err = env.ValidateVars("MDM_ACCOUNT", "MDM_NAMESPACE"); err != nil {
|
|
|
|
return nil, err
|
2022-08-18 10:48:23 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m := statsd.New(ctx, log.WithField("component", "update-ocp-versions"), _env, os.Getenv("MDM_ACCOUNT"), os.Getenv("MDM_NAMESPACE"), os.Getenv("MDM_STATSD_SOCKET"))
|
|
|
|
|
2024-07-17 09:29:39 +03:00
|
|
|
dbc, err := database.NewDatabaseClientFromEnv(ctx, _env, log, m, nil)
|
2022-08-18 10:48:23 +03:00
|
|
|
if err != nil {
|
2024-07-17 09:29:39 +03:00
|
|
|
return nil, fmt.Errorf("failed creating database client: %w", err)
|
2022-08-18 10:48:23 +03:00
|
|
|
}
|
|
|
|
|
2024-07-17 09:29:39 +03:00
|
|
|
dbName, err := env.DBName(_env)
|
2022-08-18 10:48:23 +03:00
|
|
|
if err != nil {
|
2022-08-25 10:27:17 +03:00
|
|
|
return nil, err
|
2022-08-18 10:48:23 +03:00
|
|
|
}
|
|
|
|
|
2023-06-01 15:10:36 +03:00
|
|
|
dbOpenShiftVersions, err := database.NewOpenShiftVersions(ctx, dbc, dbName)
|
2022-08-18 10:48:23 +03:00
|
|
|
if err != nil {
|
2022-08-25 10:27:17 +03:00
|
|
|
return nil, err
|
2022-08-18 10:48:23 +03:00
|
|
|
}
|
|
|
|
|
2022-08-25 10:27:17 +03:00
|
|
|
return dbOpenShiftVersions, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func updateOpenShiftVersions(ctx context.Context, dbOpenShiftVersions database.OpenShiftVersions, log *logrus.Entry) error {
|
2022-08-26 05:02:44 +03:00
|
|
|
existingVersions, err := dbOpenShiftVersions.ListAll(ctx)
|
2022-08-18 10:48:23 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-08-26 05:02:44 +03:00
|
|
|
latestVersions, err := getLatestOCPVersions(ctx, log)
|
2022-08-25 10:27:17 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-08-18 10:48:23 +03:00
|
|
|
newVersions := make(map[string]api.OpenShiftVersion)
|
2022-08-26 05:02:44 +03:00
|
|
|
for _, doc := range latestVersions {
|
2022-09-20 00:52:58 +03:00
|
|
|
newVersions[doc.Properties.Version] = doc
|
2022-08-18 10:48:23 +03:00
|
|
|
}
|
|
|
|
|
2022-08-26 05:02:44 +03:00
|
|
|
for _, doc := range existingVersions.OpenShiftVersionDocuments {
|
2022-09-20 00:52:58 +03:00
|
|
|
existing, found := newVersions[doc.OpenShiftVersion.Properties.Version]
|
2022-08-26 05:02:44 +03:00
|
|
|
if found {
|
2022-09-20 00:52:58 +03:00
|
|
|
log.Printf("Found Version %q, patching", existing.Properties.Version)
|
2022-08-26 05:02:44 +03:00
|
|
|
_, err := dbOpenShiftVersions.Patch(ctx, doc.ID, func(inFlightDoc *api.OpenShiftVersionDocument) error {
|
|
|
|
inFlightDoc.OpenShiftVersion = &existing
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2022-08-19 11:17:16 +03:00
|
|
|
}
|
2022-09-20 00:52:58 +03:00
|
|
|
log.Printf("Version %q found", existing.Properties.Version)
|
|
|
|
delete(newVersions, existing.Properties.Version)
|
2022-08-26 05:02:44 +03:00
|
|
|
continue
|
|
|
|
}
|
2022-08-19 11:17:16 +03:00
|
|
|
|
2022-09-20 00:52:58 +03:00
|
|
|
log.Printf("Version %q not found, deleting", doc.OpenShiftVersion.Properties.Version)
|
2023-11-03 15:20:55 +03:00
|
|
|
// Delete via changefeed
|
|
|
|
_, err := dbOpenShiftVersions.Patch(ctx, doc.ID,
|
|
|
|
func(d *api.OpenShiftVersionDocument) error {
|
|
|
|
d.OpenShiftVersion.Deleting = true
|
|
|
|
d.TTL = 60
|
|
|
|
return nil
|
|
|
|
})
|
2022-08-26 05:02:44 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
2022-08-18 10:48:23 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, doc := range newVersions {
|
2022-09-20 00:52:58 +03:00
|
|
|
log.Printf("Version %q not found in database, creating", doc.Properties.Version)
|
2022-08-18 10:48:23 +03:00
|
|
|
newDoc := api.OpenShiftVersionDocument{
|
2022-08-25 10:27:17 +03:00
|
|
|
ID: dbOpenShiftVersions.NewUUID(),
|
2022-08-18 10:48:23 +03:00
|
|
|
OpenShiftVersion: &doc,
|
|
|
|
}
|
|
|
|
_, err := dbOpenShiftVersions.Create(ctx, &newDoc)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2022-08-25 10:27:17 +03:00
|
|
|
|
|
|
|
func updateOCPVersions(ctx context.Context, log *logrus.Entry) error {
|
|
|
|
dbOpenShiftVersions, err := getVersionsDatabase(ctx, log)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = updateOpenShiftVersions(ctx, dbOpenShiftVersions, log)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|