-
Notifications
You must be signed in to change notification settings - Fork 4
CLOUD-861: Automate operator release #320
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c591e1e
06c3504
221623c
cd15072
53a8acd
e7c3f6a
b345ec2
bf19005
09339b3
42d83e0
267c2a8
e86480f
e9e8c9b
e5bd255
9a01dc6
0fc80cc
dff5d64
16c4f0e
49e01e5
fdc56d5
6ebf2b3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
# operator-tool | ||
|
||
`operator-tool` is designed to generate a source file for a version service. It retrieves a list of product versions from the [Percona Downloads](https://www.percona.com/downloads) API (`https://www.percona.com/products-api.php`) and searches for the corresponding images in the [Docker Hub repository](https://hub.docker.com/u/percona). If an image is not specified in the API, the latest tag of that image will be used. | ||
|
||
Build it using `make init`. | ||
|
||
## Usage | ||
|
||
### Help | ||
|
||
```sh | ||
$ ./bin/operator-tool --help | ||
Usage of ./bin/operator-tool: | ||
-cap int | ||
Sets a limit on the number of versions allowed for each major version of a product | ||
-file string | ||
Specify an older source file. The operator-tool will exclude any versions that are older than those listed in this file | ||
-include-arch-images | ||
Include images with "-multi", "-arm64", "-aarch64" suffixes in the output file | ||
-only-latest | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm using This makes backup, operator and pmm have 2 images, but ideally we want just the latest. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When This commit makes |
||
Add only latest major version images to the specified "-file". If "-file" is not specified, returns a file with latest major versions. | ||
-operator string | ||
Operator name. Available values: [psmdb pxc ps pg] | ||
-patch string | ||
Provide a path to a patch file to add additional images. Must be used together with the --file option | ||
-verbose | ||
Show logs | ||
-version string | ||
Operator version | ||
``` | ||
|
||
### Generating source file from zero | ||
|
||
```sh | ||
$ ./bin/operator-tool --operator "psmdb" --version "1.17.0" # outputs source file for psmdb-operator | ||
... | ||
$ ./bin/operator-tool --operator "pg" --version "2.5.0" # outputs source file for pg-operator | ||
... | ||
$ ./bin/operator-tool --operator "ps" --version "0.8.0" # outputs source file for ps-operator | ||
... | ||
$ ./bin/operator-tool --operator "pxc" --version "1.15.1" # outputs source file for pxc-operator | ||
... | ||
``` | ||
|
||
### Generating source file based on older file | ||
|
||
```sh | ||
$ ./bin/operator-tool --file ./sources/operator.2.5.0.pg-operator.json --version "1.17.0" # outputs source file for pg-operator, excluding older versions specified in the file | ||
... | ||
``` | ||
|
||
### Patching existing source file with a patch file | ||
|
||
```sh | ||
$ ./bin/operator-tool --file ./sources/operator.2.5.0.pg-operator.json --patch ./tools/operator-tool/patch-file.json.example | ||
... | ||
``` | ||
|
||
*Patch file example:* | ||
The example patch file can be found [here](./patch-file.json.example). | ||
|
||
```json | ||
{ | ||
"operator": { | ||
"2.4.28": { | ||
"image_path": "some-path:tag" | ||
} | ||
}, | ||
"pmm": { | ||
"2.50.1": { | ||
"image_path": "some-path:tag" | ||
} | ||
} | ||
} | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"reflect" | ||
"slices" | ||
|
||
vsAPI "github.com/Percona-Lab/percona-version-service/versionpb/api" | ||
gover "github.com/hashicorp/go-version" | ||
|
||
"operator-tool/internal/matrix" | ||
"operator-tool/internal/util" | ||
) | ||
|
||
// deleteOldVersions removes versions from the matrix that are older than those specified in the file. | ||
func deleteOldVersions(file string, m *vsAPI.VersionMatrix) error { | ||
oldestVersions, err := getOldestVersions(file) | ||
if err != nil { | ||
return fmt.Errorf("failed to get oldest versions from base file: %w", err) | ||
} | ||
|
||
return matrix.Iterate(m, func(fieldName string, fieldValue reflect.Value) error { | ||
oldestVersion, ok := oldestVersions[fieldName] | ||
if !ok { | ||
return nil | ||
} | ||
|
||
m := fieldValue.Interface().(map[string]*vsAPI.Version) | ||
if len(m) == 0 { | ||
return nil | ||
} | ||
|
||
for k := range m { | ||
if util.Goversion(k).Compare(oldestVersion) < 0 { | ||
fieldValue.SetMapIndex(reflect.ValueOf(k), reflect.Value{}) // delete old version from map | ||
} | ||
} | ||
return nil | ||
}) | ||
} | ||
|
||
func keepOnlyLatestVersions(m *vsAPI.VersionMatrix) error { | ||
return matrix.Iterate(m, func(fieldName string, fieldValue reflect.Value) error { | ||
versionMap := fieldValue.Interface().(map[string]*vsAPI.Version) | ||
if len(versionMap) == 0 { | ||
return nil | ||
} | ||
|
||
latestByMajorVer := make(map[int]string) | ||
for v := range versionMap { | ||
majorVer := util.Goversion(v).Segments()[0] | ||
|
||
curLatest, ok := latestByMajorVer[majorVer] | ||
if !ok || util.Goversion(v).Compare(util.Goversion(curLatest)) > 0 { | ||
latestByMajorVer[majorVer] = v | ||
continue | ||
} | ||
} | ||
|
||
for v := range versionMap { | ||
majorVer := util.Goversion(v).Segments()[0] | ||
if latestByMajorVer[majorVer] != v { | ||
fieldValue.SetMapIndex(reflect.ValueOf(v), reflect.Value{}) // delete non-latest version from map | ||
} | ||
} | ||
return nil | ||
}) | ||
} | ||
|
||
// getOldestVersions returns a map where each key is a struct field name from the VersionMatrix | ||
// of the specified file, and each value is the corresponding oldest version for that field. | ||
func getOldestVersions(filePath string) (map[string]*gover.Version, error) { | ||
prod, err := util.ReadBaseFile(filePath) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to read base file: %w", err) | ||
} | ||
|
||
versions := make(map[string]*gover.Version) | ||
if err := matrix.Iterate(prod.Versions[0].Matrix, func(fieldName string, fieldValue reflect.Value) error { | ||
versionMap := fieldValue.Interface().(map[string]*vsAPI.Version) | ||
if len(versionMap) == 0 { | ||
return nil | ||
} | ||
oldestVersion := "" | ||
for k := range versionMap { | ||
if oldestVersion == "" { | ||
oldestVersion = k | ||
continue | ||
} | ||
if util.Goversion(oldestVersion).Compare(util.Goversion(k)) > 0 { | ||
oldestVersion = k | ||
} | ||
} | ||
versions[fieldName] = util.Goversion(oldestVersion) | ||
return nil | ||
}); err != nil { | ||
return nil, err | ||
} | ||
|
||
return versions, nil | ||
} | ||
|
||
func limitMajorVersions(m *vsAPI.VersionMatrix, capacity int) error { | ||
if capacity <= 0 { | ||
return nil | ||
} | ||
return matrix.Iterate(m, func(fieldName string, fieldValue reflect.Value) error { | ||
versionMap := fieldValue.Interface().(map[string]*vsAPI.Version) | ||
versionsByMajorVer := make(map[int][]string) | ||
for v := range versionMap { | ||
majorVer := util.Goversion(v).Segments()[0] | ||
versionsByMajorVer[majorVer] = append(versionsByMajorVer[majorVer], v) | ||
} | ||
for _, versions := range versionsByMajorVer { | ||
if len(versions) <= capacity { | ||
return nil | ||
} | ||
slices.SortFunc(versions, func(a, b string) int { | ||
return util.Goversion(b).Compare(util.Goversion(a)) | ||
}) | ||
|
||
versionsToDelete := versions[capacity:] | ||
for _, v := range versionsToDelete { | ||
fieldValue.SetMapIndex(reflect.ValueOf(v), reflect.Value{}) | ||
} | ||
} | ||
|
||
return nil | ||
}) | ||
} | ||
|
||
// updateMatrixStatuses updates the statuses of version maps. | ||
// For each major version, it sets the highest version as "recommended" | ||
// and all other versions as "available". | ||
func updateMatrixStatuses(m *vsAPI.VersionMatrix) error { | ||
setStatus := func(vm map[string]*vsAPI.Version) { | ||
highestVersions := make(map[int]string) | ||
for version := range vm { | ||
vm[version].Status = vsAPI.Status_available | ||
|
||
majorVersion := util.Goversion(version).Segments()[0] | ||
|
||
currentHighestVersion, ok := highestVersions[majorVersion] | ||
|
||
if !ok || util.Goversion(version).Compare(util.Goversion(currentHighestVersion)) > 0 { | ||
highestVersions[majorVersion] = version | ||
} | ||
} | ||
|
||
for _, version := range highestVersions { | ||
vm[version].Status = vsAPI.Status_recommended | ||
} | ||
} | ||
|
||
return matrix.Iterate(m, func(fieldName string, fieldValue reflect.Value) error { | ||
versionMap := fieldValue.Interface().(map[string]*vsAPI.Version) | ||
if len(versionMap) == 0 { | ||
return nil | ||
} | ||
setStatus(versionMap) | ||
return nil | ||
}) | ||
} |
Uh oh!
There was an error while loading. Please reload this page.