mirror of
https://github.com/helm/helm.git
synced 2026-04-15 21:59:50 -04:00
This is part of HIP 20 which provides a means to have v3 charts that live alongside v2 charts while having breaking changes. The plan is to have a different release object for v3 chart instances for at least a couple reasons: 1. So that the chart object on the release can be fundamentally different. 2. So that Helm v3 does not detect or try to work with instances of charts whose apiVersion it does not know about. Note: it is expected that Helm v3 usage will be used long after the Helm project no longer supports it. 5 years after Helm v2 had reached end-of-life there was still usage of it. Note: The release util package is separate from the versioned elements as it is planned to use generics to handle multiple release object versions. Signed-off-by: Matt Farina <matt.farina@suse.com>
697 lines
19 KiB
Go
697 lines
19 KiB
Go
/*
|
|
Copyright The Helm Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package driver // import "helm.sh/helm/v4/pkg/storage/driver"
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/jmoiron/sqlx"
|
|
migrate "github.com/rubenv/sql-migrate"
|
|
|
|
sq "github.com/Masterminds/squirrel"
|
|
|
|
// Import pq for postgres dialect
|
|
_ "github.com/lib/pq"
|
|
|
|
rspb "helm.sh/helm/v4/pkg/release/v1"
|
|
)
|
|
|
|
var _ Driver = (*SQL)(nil)
|
|
|
|
var labelMap = map[string]struct{}{
|
|
"modifiedAt": {},
|
|
"createdAt": {},
|
|
"version": {},
|
|
"status": {},
|
|
"owner": {},
|
|
"name": {},
|
|
}
|
|
|
|
const postgreSQLDialect = "postgres"
|
|
|
|
// SQLDriverName is the string name of this driver.
|
|
const SQLDriverName = "SQL"
|
|
|
|
const sqlReleaseTableName = "releases_v1"
|
|
const sqlCustomLabelsTableName = "custom_labels_v1"
|
|
|
|
const (
|
|
sqlReleaseTableKeyColumn = "key"
|
|
sqlReleaseTableTypeColumn = "type"
|
|
sqlReleaseTableBodyColumn = "body"
|
|
sqlReleaseTableNameColumn = "name"
|
|
sqlReleaseTableNamespaceColumn = "namespace"
|
|
sqlReleaseTableVersionColumn = "version"
|
|
sqlReleaseTableStatusColumn = "status"
|
|
sqlReleaseTableOwnerColumn = "owner"
|
|
sqlReleaseTableCreatedAtColumn = "createdAt"
|
|
sqlReleaseTableModifiedAtColumn = "modifiedAt"
|
|
|
|
sqlCustomLabelsTableReleaseKeyColumn = "releaseKey"
|
|
sqlCustomLabelsTableReleaseNamespaceColumn = "releaseNamespace"
|
|
sqlCustomLabelsTableKeyColumn = "key"
|
|
sqlCustomLabelsTableValueColumn = "value"
|
|
)
|
|
|
|
// Following limits based on k8s labels limits - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#syntax-and-character-set
|
|
const (
|
|
sqlCustomLabelsTableKeyMaxLength = 253 + 1 + 63
|
|
sqlCustomLabelsTableValueMaxLength = 63
|
|
)
|
|
|
|
const (
|
|
sqlReleaseDefaultOwner = "helm"
|
|
sqlReleaseDefaultType = "helm.sh/release.v1"
|
|
)
|
|
|
|
// SQL is the sql storage driver implementation.
|
|
type SQL struct {
|
|
db *sqlx.DB
|
|
namespace string
|
|
statementBuilder sq.StatementBuilderType
|
|
|
|
Log func(string, ...interface{})
|
|
}
|
|
|
|
// Name returns the name of the driver.
|
|
func (s *SQL) Name() string {
|
|
return SQLDriverName
|
|
}
|
|
|
|
// Check if all migrations al
|
|
func (s *SQL) checkAlreadyApplied(migrations []*migrate.Migration) bool {
|
|
// make map (set) of ids for fast search
|
|
migrationsIDs := make(map[string]struct{})
|
|
for _, migration := range migrations {
|
|
migrationsIDs[migration.Id] = struct{}{}
|
|
}
|
|
|
|
// get list of applied migrations
|
|
migrate.SetDisableCreateTable(true)
|
|
records, err := migrate.GetMigrationRecords(s.db.DB, postgreSQLDialect)
|
|
migrate.SetDisableCreateTable(false)
|
|
if err != nil {
|
|
s.Log("checkAlreadyApplied: failed to get migration records: %v", err)
|
|
return false
|
|
}
|
|
|
|
for _, record := range records {
|
|
if _, ok := migrationsIDs[record.Id]; ok {
|
|
s.Log("checkAlreadyApplied: found previous migration (Id: %v) applied at %v", record.Id, record.AppliedAt)
|
|
delete(migrationsIDs, record.Id)
|
|
}
|
|
}
|
|
|
|
// check if all migrations applied
|
|
if len(migrationsIDs) != 0 {
|
|
for id := range migrationsIDs {
|
|
s.Log("checkAlreadyApplied: find unapplied migration (id: %v)", id)
|
|
}
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (s *SQL) ensureDBSetup() error {
|
|
|
|
migrations := &migrate.MemoryMigrationSource{
|
|
Migrations: []*migrate.Migration{
|
|
{
|
|
Id: "init",
|
|
Up: []string{
|
|
fmt.Sprintf(`
|
|
CREATE TABLE %s (
|
|
%s VARCHAR(90),
|
|
%s VARCHAR(64) NOT NULL,
|
|
%s TEXT NOT NULL,
|
|
%s VARCHAR(64) NOT NULL,
|
|
%s VARCHAR(64) NOT NULL,
|
|
%s INTEGER NOT NULL,
|
|
%s TEXT NOT NULL,
|
|
%s TEXT NOT NULL,
|
|
%s INTEGER NOT NULL,
|
|
%s INTEGER NOT NULL DEFAULT 0,
|
|
PRIMARY KEY(%s, %s)
|
|
);
|
|
CREATE INDEX ON %s (%s, %s);
|
|
CREATE INDEX ON %s (%s);
|
|
CREATE INDEX ON %s (%s);
|
|
CREATE INDEX ON %s (%s);
|
|
CREATE INDEX ON %s (%s);
|
|
CREATE INDEX ON %s (%s);
|
|
|
|
GRANT ALL ON %s TO PUBLIC;
|
|
|
|
ALTER TABLE %s ENABLE ROW LEVEL SECURITY;
|
|
`,
|
|
sqlReleaseTableName,
|
|
sqlReleaseTableKeyColumn,
|
|
sqlReleaseTableTypeColumn,
|
|
sqlReleaseTableBodyColumn,
|
|
sqlReleaseTableNameColumn,
|
|
sqlReleaseTableNamespaceColumn,
|
|
sqlReleaseTableVersionColumn,
|
|
sqlReleaseTableStatusColumn,
|
|
sqlReleaseTableOwnerColumn,
|
|
sqlReleaseTableCreatedAtColumn,
|
|
sqlReleaseTableModifiedAtColumn,
|
|
sqlReleaseTableKeyColumn,
|
|
sqlReleaseTableNamespaceColumn,
|
|
sqlReleaseTableName,
|
|
sqlReleaseTableKeyColumn,
|
|
sqlReleaseTableNamespaceColumn,
|
|
sqlReleaseTableName,
|
|
sqlReleaseTableVersionColumn,
|
|
sqlReleaseTableName,
|
|
sqlReleaseTableStatusColumn,
|
|
sqlReleaseTableName,
|
|
sqlReleaseTableOwnerColumn,
|
|
sqlReleaseTableName,
|
|
sqlReleaseTableCreatedAtColumn,
|
|
sqlReleaseTableName,
|
|
sqlReleaseTableModifiedAtColumn,
|
|
sqlReleaseTableName,
|
|
sqlReleaseTableName,
|
|
),
|
|
},
|
|
Down: []string{
|
|
fmt.Sprintf(`
|
|
DROP TABLE %s;
|
|
`, sqlReleaseTableName),
|
|
},
|
|
},
|
|
{
|
|
Id: "custom_labels",
|
|
Up: []string{
|
|
fmt.Sprintf(`
|
|
CREATE TABLE %s (
|
|
%s VARCHAR(64),
|
|
%s VARCHAR(67),
|
|
%s VARCHAR(%d),
|
|
%s VARCHAR(%d)
|
|
);
|
|
CREATE INDEX ON %s (%s, %s);
|
|
|
|
GRANT ALL ON %s TO PUBLIC;
|
|
ALTER TABLE %s ENABLE ROW LEVEL SECURITY;
|
|
`,
|
|
sqlCustomLabelsTableName,
|
|
sqlCustomLabelsTableReleaseKeyColumn,
|
|
sqlCustomLabelsTableReleaseNamespaceColumn,
|
|
sqlCustomLabelsTableKeyColumn,
|
|
sqlCustomLabelsTableKeyMaxLength,
|
|
sqlCustomLabelsTableValueColumn,
|
|
sqlCustomLabelsTableValueMaxLength,
|
|
sqlCustomLabelsTableName,
|
|
sqlCustomLabelsTableReleaseKeyColumn,
|
|
sqlCustomLabelsTableReleaseNamespaceColumn,
|
|
sqlCustomLabelsTableName,
|
|
sqlCustomLabelsTableName,
|
|
),
|
|
},
|
|
Down: []string{
|
|
fmt.Sprintf(`
|
|
DELETE TABLE %s;
|
|
`, sqlCustomLabelsTableName),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
// Check that init migration already applied
|
|
if s.checkAlreadyApplied(migrations.Migrations) {
|
|
return nil
|
|
}
|
|
|
|
// Populate the database with the relations we need if they don't exist yet
|
|
_, err := migrate.Exec(s.db.DB, postgreSQLDialect, migrations, migrate.Up)
|
|
return err
|
|
}
|
|
|
|
// SQLReleaseWrapper describes how Helm releases are stored in an SQL database
|
|
type SQLReleaseWrapper struct {
|
|
// The primary key, made of {release-name}.{release-version}
|
|
Key string `db:"key"`
|
|
|
|
// See https://github.com/helm/helm/blob/c9fe3d118caec699eb2565df9838673af379ce12/pkg/storage/driver/secrets.go#L231
|
|
Type string `db:"type"`
|
|
|
|
// The rspb.Release body, as a base64-encoded string
|
|
Body string `db:"body"`
|
|
|
|
// Release "labels" that can be used as filters in the storage.Query(labels map[string]string)
|
|
// we implemented. Note that allowing Helm users to filter against new dimensions will require a
|
|
// new migration to be added, and the Create and/or update functions to be updated accordingly.
|
|
Name string `db:"name"`
|
|
Namespace string `db:"namespace"`
|
|
Version int `db:"version"`
|
|
Status string `db:"status"`
|
|
Owner string `db:"owner"`
|
|
CreatedAt int `db:"createdAt"`
|
|
ModifiedAt int `db:"modifiedAt"`
|
|
}
|
|
|
|
type SQLReleaseCustomLabelWrapper struct {
|
|
ReleaseKey string `db:"release_key"`
|
|
ReleaseNamespace string `db:"release_namespace"`
|
|
Key string `db:"key"`
|
|
Value string `db:"value"`
|
|
}
|
|
|
|
// NewSQL initializes a new sql driver.
|
|
func NewSQL(connectionString string, logger func(string, ...interface{}), namespace string) (*SQL, error) {
|
|
db, err := sqlx.Connect(postgreSQLDialect, connectionString)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
driver := &SQL{
|
|
db: db,
|
|
Log: logger,
|
|
statementBuilder: sq.StatementBuilder.PlaceholderFormat(sq.Dollar),
|
|
}
|
|
|
|
if err := driver.ensureDBSetup(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
driver.namespace = namespace
|
|
|
|
return driver, nil
|
|
}
|
|
|
|
// Get returns the release named by key.
|
|
func (s *SQL) Get(key string) (*rspb.Release, error) {
|
|
var record SQLReleaseWrapper
|
|
|
|
qb := s.statementBuilder.
|
|
Select(sqlReleaseTableBodyColumn).
|
|
From(sqlReleaseTableName).
|
|
Where(sq.Eq{sqlReleaseTableKeyColumn: key}).
|
|
Where(sq.Eq{sqlReleaseTableNamespaceColumn: s.namespace})
|
|
|
|
query, args, err := qb.ToSql()
|
|
if err != nil {
|
|
s.Log("failed to build query: %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
// Get will return an error if the result is empty
|
|
if err := s.db.Get(&record, query, args...); err != nil {
|
|
s.Log("got SQL error when getting release %s: %v", key, err)
|
|
return nil, ErrReleaseNotFound
|
|
}
|
|
|
|
release, err := decodeRelease(record.Body)
|
|
if err != nil {
|
|
s.Log("get: failed to decode data %q: %v", key, err)
|
|
return nil, err
|
|
}
|
|
|
|
if release.Labels, err = s.getReleaseCustomLabels(key, s.namespace); err != nil {
|
|
s.Log("failed to get release %s/%s custom labels: %v", s.namespace, key, err)
|
|
return nil, err
|
|
}
|
|
|
|
return release, nil
|
|
}
|
|
|
|
// List returns the list of all releases such that filter(release) == true
|
|
func (s *SQL) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) {
|
|
sb := s.statementBuilder.
|
|
Select(sqlReleaseTableKeyColumn, sqlReleaseTableNamespaceColumn, sqlReleaseTableBodyColumn).
|
|
From(sqlReleaseTableName).
|
|
Where(sq.Eq{sqlReleaseTableOwnerColumn: sqlReleaseDefaultOwner})
|
|
|
|
// If a namespace was specified, we only list releases from that namespace
|
|
if s.namespace != "" {
|
|
sb = sb.Where(sq.Eq{sqlReleaseTableNamespaceColumn: s.namespace})
|
|
}
|
|
|
|
query, args, err := sb.ToSql()
|
|
if err != nil {
|
|
s.Log("failed to build query: %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
var records = []SQLReleaseWrapper{}
|
|
if err := s.db.Select(&records, query, args...); err != nil {
|
|
s.Log("list: failed to list: %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
var releases []*rspb.Release
|
|
for _, record := range records {
|
|
release, err := decodeRelease(record.Body)
|
|
if err != nil {
|
|
s.Log("list: failed to decode release: %v: %v", record, err)
|
|
continue
|
|
}
|
|
|
|
if release.Labels, err = s.getReleaseCustomLabels(record.Key, record.Namespace); err != nil {
|
|
s.Log("failed to get release %s/%s custom labels: %v", record.Namespace, record.Key, err)
|
|
return nil, err
|
|
}
|
|
for k, v := range getReleaseSystemLabels(release) {
|
|
release.Labels[k] = v
|
|
}
|
|
|
|
if filter(release) {
|
|
releases = append(releases, release)
|
|
}
|
|
}
|
|
|
|
return releases, nil
|
|
}
|
|
|
|
// Query returns the set of releases that match the provided set of labels.
|
|
func (s *SQL) Query(labels map[string]string) ([]*rspb.Release, error) {
|
|
sb := s.statementBuilder.
|
|
Select(sqlReleaseTableKeyColumn, sqlReleaseTableNamespaceColumn, sqlReleaseTableBodyColumn).
|
|
From(sqlReleaseTableName)
|
|
|
|
keys := make([]string, 0, len(labels))
|
|
for key := range labels {
|
|
keys = append(keys, key)
|
|
}
|
|
sort.Strings(keys)
|
|
for _, key := range keys {
|
|
if _, ok := labelMap[key]; ok {
|
|
sb = sb.Where(sq.Eq{key: labels[key]})
|
|
} else {
|
|
s.Log("unknown label %s", key)
|
|
return nil, fmt.Errorf("unknown label %s", key)
|
|
}
|
|
}
|
|
|
|
// If a namespace was specified, we only list releases from that namespace
|
|
if s.namespace != "" {
|
|
sb = sb.Where(sq.Eq{sqlReleaseTableNamespaceColumn: s.namespace})
|
|
}
|
|
|
|
// Build our query
|
|
query, args, err := sb.ToSql()
|
|
if err != nil {
|
|
s.Log("failed to build query: %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
var records = []SQLReleaseWrapper{}
|
|
if err := s.db.Select(&records, query, args...); err != nil {
|
|
s.Log("list: failed to query with labels: %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
if len(records) == 0 {
|
|
return nil, ErrReleaseNotFound
|
|
}
|
|
|
|
var releases []*rspb.Release
|
|
for _, record := range records {
|
|
release, err := decodeRelease(record.Body)
|
|
if err != nil {
|
|
s.Log("list: failed to decode release: %v: %v", record, err)
|
|
continue
|
|
}
|
|
|
|
if release.Labels, err = s.getReleaseCustomLabels(record.Key, record.Namespace); err != nil {
|
|
s.Log("failed to get release %s/%s custom labels: %v", record.Namespace, record.Key, err)
|
|
return nil, err
|
|
}
|
|
|
|
releases = append(releases, release)
|
|
}
|
|
|
|
if len(releases) == 0 {
|
|
return nil, ErrReleaseNotFound
|
|
}
|
|
|
|
return releases, nil
|
|
}
|
|
|
|
// Create creates a new release.
|
|
func (s *SQL) Create(key string, rls *rspb.Release) error {
|
|
namespace := rls.Namespace
|
|
if namespace == "" {
|
|
namespace = defaultNamespace
|
|
}
|
|
s.namespace = namespace
|
|
|
|
body, err := encodeRelease(rls)
|
|
if err != nil {
|
|
s.Log("failed to encode release: %v", err)
|
|
return err
|
|
}
|
|
|
|
transaction, err := s.db.Beginx()
|
|
if err != nil {
|
|
s.Log("failed to start SQL transaction: %v", err)
|
|
return fmt.Errorf("error beginning transaction: %v", err)
|
|
}
|
|
|
|
insertQuery, args, err := s.statementBuilder.
|
|
Insert(sqlReleaseTableName).
|
|
Columns(
|
|
sqlReleaseTableKeyColumn,
|
|
sqlReleaseTableTypeColumn,
|
|
sqlReleaseTableBodyColumn,
|
|
sqlReleaseTableNameColumn,
|
|
sqlReleaseTableNamespaceColumn,
|
|
sqlReleaseTableVersionColumn,
|
|
sqlReleaseTableStatusColumn,
|
|
sqlReleaseTableOwnerColumn,
|
|
sqlReleaseTableCreatedAtColumn,
|
|
).
|
|
Values(
|
|
key,
|
|
sqlReleaseDefaultType,
|
|
body,
|
|
rls.Name,
|
|
namespace,
|
|
int(rls.Version),
|
|
rls.Info.Status.String(),
|
|
sqlReleaseDefaultOwner,
|
|
int(time.Now().Unix()),
|
|
).ToSql()
|
|
if err != nil {
|
|
s.Log("failed to build insert query: %v", err)
|
|
return err
|
|
}
|
|
|
|
if _, err := transaction.Exec(insertQuery, args...); err != nil {
|
|
defer transaction.Rollback()
|
|
|
|
selectQuery, args, buildErr := s.statementBuilder.
|
|
Select(sqlReleaseTableKeyColumn).
|
|
From(sqlReleaseTableName).
|
|
Where(sq.Eq{sqlReleaseTableKeyColumn: key}).
|
|
Where(sq.Eq{sqlReleaseTableNamespaceColumn: s.namespace}).
|
|
ToSql()
|
|
if buildErr != nil {
|
|
s.Log("failed to build select query: %v", buildErr)
|
|
return err
|
|
}
|
|
|
|
var record SQLReleaseWrapper
|
|
if err := transaction.Get(&record, selectQuery, args...); err == nil {
|
|
s.Log("release %s already exists", key)
|
|
return ErrReleaseExists
|
|
}
|
|
|
|
s.Log("failed to store release %s in SQL database: %v", key, err)
|
|
return err
|
|
}
|
|
|
|
// Filtering labels before insert cause in SQL storage driver system releases are stored in separate columns of release table
|
|
for k, v := range filterSystemLabels(rls.Labels) {
|
|
insertLabelsQuery, args, err := s.statementBuilder.
|
|
Insert(sqlCustomLabelsTableName).
|
|
Columns(
|
|
sqlCustomLabelsTableReleaseKeyColumn,
|
|
sqlCustomLabelsTableReleaseNamespaceColumn,
|
|
sqlCustomLabelsTableKeyColumn,
|
|
sqlCustomLabelsTableValueColumn,
|
|
).
|
|
Values(
|
|
key,
|
|
namespace,
|
|
k,
|
|
v,
|
|
).ToSql()
|
|
|
|
if err != nil {
|
|
defer transaction.Rollback()
|
|
s.Log("failed to build insert query: %v", err)
|
|
return err
|
|
}
|
|
|
|
if _, err := transaction.Exec(insertLabelsQuery, args...); err != nil {
|
|
defer transaction.Rollback()
|
|
s.Log("failed to write Labels: %v", err)
|
|
return err
|
|
}
|
|
}
|
|
defer transaction.Commit()
|
|
|
|
return nil
|
|
}
|
|
|
|
// Update updates a release.
|
|
func (s *SQL) Update(key string, rls *rspb.Release) error {
|
|
namespace := rls.Namespace
|
|
if namespace == "" {
|
|
namespace = defaultNamespace
|
|
}
|
|
s.namespace = namespace
|
|
|
|
body, err := encodeRelease(rls)
|
|
if err != nil {
|
|
s.Log("failed to encode release: %v", err)
|
|
return err
|
|
}
|
|
|
|
query, args, err := s.statementBuilder.
|
|
Update(sqlReleaseTableName).
|
|
Set(sqlReleaseTableBodyColumn, body).
|
|
Set(sqlReleaseTableNameColumn, rls.Name).
|
|
Set(sqlReleaseTableVersionColumn, int(rls.Version)).
|
|
Set(sqlReleaseTableStatusColumn, rls.Info.Status.String()).
|
|
Set(sqlReleaseTableOwnerColumn, sqlReleaseDefaultOwner).
|
|
Set(sqlReleaseTableModifiedAtColumn, int(time.Now().Unix())).
|
|
Where(sq.Eq{sqlReleaseTableKeyColumn: key}).
|
|
Where(sq.Eq{sqlReleaseTableNamespaceColumn: namespace}).
|
|
ToSql()
|
|
|
|
if err != nil {
|
|
s.Log("failed to build update query: %v", err)
|
|
return err
|
|
}
|
|
|
|
if _, err := s.db.Exec(query, args...); err != nil {
|
|
s.Log("failed to update release %s in SQL database: %v", key, err)
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Delete deletes a release or returns ErrReleaseNotFound.
|
|
func (s *SQL) Delete(key string) (*rspb.Release, error) {
|
|
transaction, err := s.db.Beginx()
|
|
if err != nil {
|
|
s.Log("failed to start SQL transaction: %v", err)
|
|
return nil, fmt.Errorf("error beginning transaction: %v", err)
|
|
}
|
|
|
|
selectQuery, args, err := s.statementBuilder.
|
|
Select(sqlReleaseTableBodyColumn).
|
|
From(sqlReleaseTableName).
|
|
Where(sq.Eq{sqlReleaseTableKeyColumn: key}).
|
|
Where(sq.Eq{sqlReleaseTableNamespaceColumn: s.namespace}).
|
|
ToSql()
|
|
if err != nil {
|
|
s.Log("failed to build select query: %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
var record SQLReleaseWrapper
|
|
err = transaction.Get(&record, selectQuery, args...)
|
|
if err != nil {
|
|
s.Log("release %s not found: %v", key, err)
|
|
return nil, ErrReleaseNotFound
|
|
}
|
|
|
|
release, err := decodeRelease(record.Body)
|
|
if err != nil {
|
|
s.Log("failed to decode release %s: %v", key, err)
|
|
transaction.Rollback()
|
|
return nil, err
|
|
}
|
|
defer transaction.Commit()
|
|
|
|
deleteQuery, args, err := s.statementBuilder.
|
|
Delete(sqlReleaseTableName).
|
|
Where(sq.Eq{sqlReleaseTableKeyColumn: key}).
|
|
Where(sq.Eq{sqlReleaseTableNamespaceColumn: s.namespace}).
|
|
ToSql()
|
|
if err != nil {
|
|
s.Log("failed to build delete query: %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
_, err = transaction.Exec(deleteQuery, args...)
|
|
if err != nil {
|
|
s.Log("failed perform delete query: %v", err)
|
|
return release, err
|
|
}
|
|
|
|
if release.Labels, err = s.getReleaseCustomLabels(key, s.namespace); err != nil {
|
|
s.Log("failed to get release %s/%s custom labels: %v", s.namespace, key, err)
|
|
return nil, err
|
|
}
|
|
|
|
deleteCustomLabelsQuery, args, err := s.statementBuilder.
|
|
Delete(sqlCustomLabelsTableName).
|
|
Where(sq.Eq{sqlCustomLabelsTableReleaseKeyColumn: key}).
|
|
Where(sq.Eq{sqlCustomLabelsTableReleaseNamespaceColumn: s.namespace}).
|
|
ToSql()
|
|
|
|
if err != nil {
|
|
s.Log("failed to build delete Labels query: %v", err)
|
|
return nil, err
|
|
}
|
|
_, err = transaction.Exec(deleteCustomLabelsQuery, args...)
|
|
return release, err
|
|
}
|
|
|
|
// Get release custom labels from database
|
|
func (s *SQL) getReleaseCustomLabels(key string, _ string) (map[string]string, error) {
|
|
query, args, err := s.statementBuilder.
|
|
Select(sqlCustomLabelsTableKeyColumn, sqlCustomLabelsTableValueColumn).
|
|
From(sqlCustomLabelsTableName).
|
|
Where(sq.Eq{sqlCustomLabelsTableReleaseKeyColumn: key,
|
|
sqlCustomLabelsTableReleaseNamespaceColumn: s.namespace}).
|
|
ToSql()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var labelsList = []SQLReleaseCustomLabelWrapper{}
|
|
if err := s.db.Select(&labelsList, query, args...); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
labelsMap := make(map[string]string)
|
|
for _, i := range labelsList {
|
|
labelsMap[i.Key] = i.Value
|
|
}
|
|
|
|
return filterSystemLabels(labelsMap), nil
|
|
}
|
|
|
|
// Rebuild system labels from release object
|
|
func getReleaseSystemLabels(rls *rspb.Release) map[string]string {
|
|
return map[string]string{
|
|
"name": rls.Name,
|
|
"owner": sqlReleaseDefaultOwner,
|
|
"status": rls.Info.Status.String(),
|
|
"version": strconv.Itoa(rls.Version),
|
|
}
|
|
}
|