diff --git a/pkg/storage/driver/mock_test.go b/pkg/storage/driver/mock_test.go index 6bae94424..08aa031f9 100644 --- a/pkg/storage/driver/mock_test.go +++ b/pkg/storage/driver/mock_test.go @@ -257,6 +257,6 @@ func newTestFixtureSQL(t *testing.T, releases ...*rspb.Release) (*SQL, sqlmock.S sqlxDB := sqlx.NewDb(sqlDB, "sqlmock") return &SQL{ db: sqlxDB, - Log: func(_ string, _ ...interface{}) {}, + Log: func(a string, b ...interface{}) {}, }, mock } diff --git a/pkg/storage/driver/sql.go b/pkg/storage/driver/sql.go index 1e7cff9d6..a70ba4940 100644 --- a/pkg/storage/driver/sql.go +++ b/pkg/storage/driver/sql.go @@ -51,9 +51,31 @@ var supportedSQLDialects = map[string]struct{}{ // SQLDriverName is the string name of this driver. const SQLDriverName = "SQL" +const sqlReleaseTableName = "releases.v1" + +const ( + sqlReleaseTableKeyColumn = "key" + sqlReleaseTableTypeColumn = "type" + sqlReleaseTableBodyColumn = "body" + sqlReleaseTableNameColumn = "name" + sqlReleaseTableNamespaceColumn = "namespace" + sqlReleaseTableVersionColumn = "version" + sqlReleaseTableStatusColumn = "status" + sqlReleaseTableOwnerColumn = "owner" + sqlReleaseTableCreatedAtColumn = "created_at" + sqlReleaseTableModifiedAtColumn = "modifiedAt" +) + +const ( + sqlReleaseDefaultOwner = "helm" + sqlReleaseDefaultType = "helm.sh/release.v1" +) + // SQL is the sql storage driver implementation. type SQL struct { - db *sqlx.DB + db *sqlx.DB + namespace string + Log func(string, ...interface{}) } @@ -70,28 +92,34 @@ func (s *SQL) ensureDBSetup() error { Id: "init", Up: []string{ ` - CREATE TABLE releases ( - key VARCHAR(67) PRIMARY KEY, + CREATE TABLE releases.v1 ( + key VARCHAR(67), type VARCHAR(64) NOT NULL, - body TEXT NOT NULL, - name VARCHAR(64) NOT NULL, + body TEXT NOT NULL, + name VARCHAR(64) NOT NULL, + namespace VARCHAR(64) NOT NULL, version INTEGER NOT NULL, status TEXT NOT NULL, owner TEXT NOT NULL, createdAt INTEGER NOT NULL, - modifiedAt INTEGER NOT NULL DEFAULT 0 + modifiedAt INTEGER NOT NULL DEFAULT 0, + PRIMARY KEY(name, namespace) ); - CREATE INDEX ON releases (key); - CREATE INDEX ON releases (version); - CREATE INDEX ON releases (status); - CREATE INDEX ON releases (owner); - CREATE INDEX ON releases (createdAt); - CREATE INDEX ON releases (modifiedAt); + CREATE INDEX ON releases.v1 (name, namespace); + CREATE INDEX ON releases.v1 (version); + CREATE INDEX ON releases.v1 (status); + CREATE INDEX ON releases.v1 (owner); + CREATE INDEX ON releases.v1 (createdAt); + CREATE INDEX ON releases.v1 (modifiedAt); + + GRANT ALL ON releases.v1 TO PUBLIC; + + ALTER TABLE releases.v1 ENABLE ROW LEVEL SECURITY; `, }, Down: []string{ ` - DROP TABLE releases; + DROP TABLE releases.v1; `, }, }, @@ -117,6 +145,7 @@ type SQLReleaseWrapper struct { // 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"` @@ -150,9 +179,24 @@ func NewSQL(dialect, connectionString string, logger func(string, ...interface{} // Get returns the release named by key. func (s *SQL) Get(key string) (*rspb.Release, error) { var record SQLReleaseWrapper + + // We first update the current namespace + var err error + if s.namespace, err = getCurrentNamespace(); err != nil { + s.Log("an error occurred while trying to get the current namespace: %v", err) + return nil, err + } + + query := fmt.Sprintf( + "SELECT %s FROM %s WHERE %s = $1 AND %s = $2", + sqlReleaseTableBodyColumn, + sqlReleaseTableName, + sqlReleaseTableKeyColumn, + sqlReleaseTableNamespaceColumn, + ) + // Get will return an error if the result is empty - err := s.db.Get(&record, "SELECT body FROM releases WHERE key = $1", key) - if err != nil { + if err := s.db.Get(&record, query, key, s.namespace); err != nil { s.Log("got SQL error when getting release %s: %v", key, err) return nil, storageerrors.ErrReleaseNotFound(key) } @@ -168,8 +212,16 @@ func (s *SQL) Get(key string) (*rspb.Release, error) { // List returns the list of all releases such that filter(release) == true func (s *SQL) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) { + query := fmt.Sprintf( + "SELECT %s FROM %s WHERE %s = '%s'", + sqlReleaseTableBodyColumn, + sqlReleaseTableName, + sqlReleaseTableOwnerColumn, + sqlReleaseDefaultOwner, + ) + var records = []SQLReleaseWrapper{} - if err := s.db.Select(&records, "SELECT body FROM releases WHERE owner = 'helm'"); err != nil { + if err := s.db.Select(&records, query); err != nil { s.Log("list: failed to list: %v", err) return nil, err } @@ -205,11 +257,24 @@ func (s *SQL) Query(labels map[string]string) ([]*rspb.Release, error) { return nil, fmt.Errorf("unknow label %s", key) } } + + // We filter out releases that do not belong to the current namespace + sqlFilterKeys = append(sqlFilterKeys, fmt.Sprintf("%s=:namespace", sqlReleaseTableNamespaceColumn)) + + // Then we update the current namespace + var err error + if s.namespace, err = getCurrentNamespace(); err != nil { + s.Log("an error occurred while trying to get the current namespace: %v", err) + return nil, err + } + + sqlFilter["namespace"] = s.namespace + sort.Strings(sqlFilterKeys) // Build our query query := strings.Join([]string{ - "SELECT body FROM releases", + fmt.Sprintf("SELECT %s FROM %s", sqlReleaseTableBodyColumn, sqlReleaseTableName), "WHERE", strings.Join(sqlFilterKeys, " AND "), }, " ") @@ -251,28 +316,58 @@ func (s *SQL) Create(key string, rls *rspb.Release) error { return err } + // We update the current namespace + if s.namespace, err = getCurrentNamespace(); err != nil { + s.Log("an error occurred while trying to get the current namespace: %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) } - if _, err := transaction.NamedExec("INSERT INTO releases (key, type, body, name, version, status, owner, createdAt) VALUES (:key, :type, :body, :name, :version, :status, :owner, :createdAt)", + query := fmt.Sprintf( + "INSERT INTO %s (%s, %s, %s, %s, %s, %s, %s, %s, %s) VALUES (:key, :type, :body, :name, :namespace, :version, :status, :owner, :createdAt)", + sqlReleaseTableName, + sqlReleaseTableKeyColumn, + sqlReleaseTableTypeColumn, + sqlReleaseTableBodyColumn, + sqlReleaseTableNameColumn, + sqlReleaseTableNamespaceColumn, + sqlReleaseTableVersionColumn, + sqlReleaseTableStatusColumn, + sqlReleaseTableOwnerColumn, + sqlReleaseTableCreatedAtColumn, + ) + + if _, err := transaction.NamedExec(query, &SQLReleaseWrapper{ Key: key, - Type: "helm.sh/release.v1", + Type: sqlReleaseDefaultType, Body: body, Name: rls.Name, + Namespace: rls.Namespace, Version: int(rls.Version), Status: rls.Info.Status.String(), - Owner: "helm", + Owner: sqlReleaseDefaultOwner, CreatedAt: int(time.Now().Unix()), }, ); err != nil { defer transaction.Rollback() + + query := fmt.Sprintf( + "SELECT %s FROM %s WHERE %s = $1 and %s = $2", + sqlReleaseTableKeyColumn, + sqlReleaseTableName, + sqlReleaseTableKeyColumn, + sqlReleaseTableNamespaceColumn, + ) + var record SQLReleaseWrapper - if err := transaction.Get(&record, "SELECT key FROM releases WHERE key = ?", key); err == nil { + if err := transaction.Get(&record, query, key, s.namespace); err == nil { s.Log("release %s already exists", key) return storageerrors.ErrReleaseExists(key) } @@ -293,14 +388,28 @@ func (s *SQL) Update(key string, rls *rspb.Release) error { return err } - if _, err := s.db.NamedExec("UPDATE releases SET body=:body, name=:name, version=:version, status=:status, owner=:owner, modifiedAt=:modifiedAt WHERE key=:key", + query := fmt.Sprintf( + "UPDATE %s SET %s=:body, %s=:name, %s=:version, %s=:status, %s=:owner, %s=:modifiedAt WHERE %s=:key AND %s=:namespace", + sqlReleaseTableName, + sqlReleaseTableBodyColumn, + sqlReleaseTableNameColumn, + sqlReleaseTableVersionColumn, + sqlReleaseTableStatusColumn, + sqlReleaseTableOwnerColumn, + sqlReleaseTableModifiedAtColumn, + sqlReleaseTableKeyColumn, + sqlReleaseTableNamespaceColumn, + ) + + if _, err := s.db.NamedExec(query, &SQLReleaseWrapper{ Key: key, Body: body, Name: rls.Name, + Namespace: rls.Namespace, Version: int(rls.Version), Status: rls.Info.Status.String(), - Owner: "helm", + Owner: sqlReleaseDefaultOwner, ModifiedAt: int(time.Now().Unix()), }, ); err != nil { @@ -319,8 +428,22 @@ func (s *SQL) Delete(key string) (*rspb.Release, error) { return nil, fmt.Errorf("error beginning transaction: %v", err) } + // We update the current namespace + if s.namespace, err = getCurrentNamespace(); err != nil { + s.Log("an error occurred while trying to get the current namespace: %v", err) + return nil, err + } + + selectQuery := fmt.Sprintf( + "SELECT %s FROM %s WHERE %s=$1 AND %s=$2", + sqlReleaseTableBodyColumn, + sqlReleaseTableName, + sqlReleaseTableKeyColumn, + sqlReleaseTableNamespaceColumn, + ) + var record SQLReleaseWrapper - err = transaction.Get(&record, "SELECT body FROM releases WHERE key = $1", key) + err = transaction.Get(&record, selectQuery, key, s.namespace) if err != nil { s.Log("release %s not found: %v", key, err) return nil, storageerrors.ErrReleaseNotFound(key) @@ -334,6 +457,13 @@ func (s *SQL) Delete(key string) (*rspb.Release, error) { } defer transaction.Commit() - _, err = transaction.Exec("DELETE FROM releases WHERE key = $1", key) + deleteQuery := fmt.Sprintf( + "DELETE FROM %s WHERE %s = $1 AND %s = $2", + sqlReleaseTableName, + sqlReleaseTableKeyColumn, + sqlReleaseTableNamespaceColumn, + ) + + _, err = transaction.Exec(deleteQuery, key, s.namespace) return release, err } diff --git a/pkg/storage/driver/sql_test.go b/pkg/storage/driver/sql_test.go index 3a87ff63d..11f8fb861 100644 --- a/pkg/storage/driver/sql_test.go +++ b/pkg/storage/driver/sql_test.go @@ -35,19 +35,33 @@ func TestSQLName(t *testing.T) { func TestSQLGet(t *testing.T) { vers := int(1) name := "smug-pigeon" - namespace := "default" + + namespace, err := getCurrentNamespace() + if err != nil { + t.Fatalf("could not get namespace: %v", err) + } + key := testKey(name, vers) rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) body, _ := encodeRelease(rel) sqlDriver, mock := newTestFixtureSQL(t) + + query := fmt.Sprintf( + regexp.QuoteMeta("SELECT %s FROM %s WHERE %s = $1 AND %s = $2"), + sqlReleaseTableBodyColumn, + sqlReleaseTableName, + sqlReleaseTableKeyColumn, + sqlReleaseTableNamespaceColumn, + ) + mock. - ExpectQuery("SELECT body FROM releases WHERE key = ?"). - WithArgs(key). + ExpectQuery(query). + WithArgs(key, namespace). WillReturnRows( mock.NewRows([]string{ - "body", + sqlReleaseTableBodyColumn, }).AddRow( body, ), @@ -78,11 +92,19 @@ func TestSQLList(t *testing.T) { sqlDriver, mock := newTestFixtureSQL(t) for i := 0; i < 3; i++ { + query := fmt.Sprintf( + "SELECT %s FROM %s WHERE %s = '%s'", + sqlReleaseTableBodyColumn, + sqlReleaseTableName, + sqlReleaseTableOwnerColumn, + sqlReleaseDefaultOwner, + ) + mock. - ExpectQuery("SELECT body FROM releases WHERE owner = 'helm'"). + ExpectQuery(query). WillReturnRows( mock.NewRows([]string{ - "body", + sqlReleaseTableBodyColumn, }). AddRow(body1). AddRow(body2). @@ -137,17 +159,36 @@ func TestSQLList(t *testing.T) { func TestSqlCreate(t *testing.T) { vers := 1 name := "smug-pigeon" - namespace := "default" + + namespace, err := getCurrentNamespace() + if err != nil { + t.Fatalf("could not get namespace: %v", err) + } + key := testKey(name, vers) rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) sqlDriver, mock := newTestFixtureSQL(t) body, _ := encodeRelease(rel) + query := fmt.Sprintf( + "INSERT INTO %s (%s, %s, %s, %s, %s, %s, %s, %s, %s) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", + sqlReleaseTableName, + sqlReleaseTableKeyColumn, + sqlReleaseTableTypeColumn, + sqlReleaseTableBodyColumn, + sqlReleaseTableNameColumn, + sqlReleaseTableNamespaceColumn, + sqlReleaseTableVersionColumn, + sqlReleaseTableStatusColumn, + sqlReleaseTableOwnerColumn, + sqlReleaseTableCreatedAtColumn, + ) + mock.ExpectBegin() mock. - ExpectExec(regexp.QuoteMeta("INSERT INTO releases (key, type, body, name, version, status, owner, createdAt) VALUES (?, ?, ?, ?, ?, ?, ?, ?)")). - WithArgs(key, "helm.sh/release.v1", body, rel.Name, int(rel.Version), rel.Info.Status.String(), "helm", int(time.Now().Unix())). + ExpectExec(regexp.QuoteMeta(query)). + WithArgs(key, sqlReleaseDefaultType, body, rel.Name, rel.Namespace, int(rel.Version), rel.Info.Status.String(), sqlReleaseDefaultOwner, int(time.Now().Unix())). WillReturnResult(sqlmock.NewResult(1, 1)) mock.ExpectCommit() @@ -163,29 +204,56 @@ func TestSqlCreate(t *testing.T) { func TestSqlCreateAlreadyExists(t *testing.T) { vers := 1 name := "smug-pigeon" - namespace := "default" + + namespace, err := getCurrentNamespace() + if err != nil { + t.Fatalf("could not get namespace: %v", err) + } + key := testKey(name, vers) rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) sqlDriver, mock := newTestFixtureSQL(t) body, _ := encodeRelease(rel) + insertQuery := fmt.Sprintf( + "INSERT INTO %s (%s, %s, %s, %s, %s, %s, %s, %s, %s) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", + sqlReleaseTableName, + sqlReleaseTableKeyColumn, + sqlReleaseTableTypeColumn, + sqlReleaseTableBodyColumn, + sqlReleaseTableNameColumn, + sqlReleaseTableNamespaceColumn, + sqlReleaseTableVersionColumn, + sqlReleaseTableStatusColumn, + sqlReleaseTableOwnerColumn, + sqlReleaseTableCreatedAtColumn, + ) + // Insert fails (primary key already exists) mock.ExpectBegin() mock. - ExpectExec(regexp.QuoteMeta("INSERT INTO releases (key, type, body, name, version, status, owner, createdAt) VALUES (?, ?, ?, ?, ?, ?, ?, ?)")). - WithArgs(key, "helm.sh/release.v1", body, rel.Name, int(rel.Version), rel.Info.Status.String(), "helm", int(time.Now().Unix())). + ExpectExec(regexp.QuoteMeta(insertQuery)). + WithArgs(key, sqlReleaseDefaultType, body, rel.Name, rel.Namespace, int(rel.Version), rel.Info.Status.String(), sqlReleaseDefaultOwner, int(time.Now().Unix())). WillReturnError(fmt.Errorf("dialect dependent SQL error")) + selectQuery := fmt.Sprintf( + regexp.QuoteMeta("SELECT %s FROM %s WHERE %s = $1 and %s = $2"), + sqlReleaseTableKeyColumn, + sqlReleaseTableName, + sqlReleaseTableKeyColumn, + sqlReleaseTableNamespaceColumn, + ) + // Let's check that we do make sure the error is due to a release already existing mock. - ExpectQuery(regexp.QuoteMeta("SELECT key FROM releases WHERE key = ?")). - WithArgs(key). + ExpectQuery(selectQuery). + WithArgs(key, namespace). WillReturnRows( mock.NewRows([]string{ - "body", + sqlReleaseTableKeyColumn, }).AddRow( - body, + key, ), ).RowsWillBeClosed() mock.ExpectRollback() @@ -202,16 +270,34 @@ func TestSqlCreateAlreadyExists(t *testing.T) { func TestSqlUpdate(t *testing.T) { vers := 1 name := "smug-pigeon" - namespace := "default" + + namespace, err := getCurrentNamespace() + if err != nil { + t.Fatalf("could not get namespace: %v", err) + } + key := testKey(name, vers) rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) sqlDriver, mock := newTestFixtureSQL(t) body, _ := encodeRelease(rel) + query := fmt.Sprintf( + "UPDATE %s SET %s=?, %s=?, %s=?, %s=?, %s=?, %s=? WHERE %s=? AND %s=?", + sqlReleaseTableName, + sqlReleaseTableBodyColumn, + sqlReleaseTableNameColumn, + sqlReleaseTableVersionColumn, + sqlReleaseTableStatusColumn, + sqlReleaseTableOwnerColumn, + sqlReleaseTableModifiedAtColumn, + sqlReleaseTableKeyColumn, + sqlReleaseTableNamespaceColumn, + ) + mock. - ExpectExec(regexp.QuoteMeta("UPDATE releases SET body=?, name=?, version=?, status=?, owner=?, modifiedAt=? WHERE key=?")). - WithArgs(body, rel.Name, int(rel.Version), rel.Info.Status.String(), "helm", int(time.Now().Unix()), key). + ExpectExec(regexp.QuoteMeta(query)). + WithArgs(body, rel.Name, int(rel.Version), rel.Info.Status.String(), sqlReleaseDefaultOwner, int(time.Now().Unix()), key, namespace). WillReturnResult(sqlmock.NewResult(0, 1)) if err := sqlDriver.Update(key, rel); err != nil { @@ -224,15 +310,20 @@ func TestSqlUpdate(t *testing.T) { } func TestSqlQuery(t *testing.T) { + namespace, err := getCurrentNamespace() + if err != nil { + t.Fatalf("could not get namespace: %v", err) + } + // Reflect actual use cases in ../storage.go labelSetDeployed := map[string]string{ "name": "smug-pigeon", - "owner": "helm", + "owner": sqlReleaseDefaultOwner, "status": "deployed", } labelSetAll := map[string]string{ "name": "smug-pigeon", - "owner": "helm", + "owner": sqlReleaseDefaultOwner, } supersededRelease := releaseStub("smug-pigeon", 1, "default", rspb.StatusSuperseded) @@ -243,23 +334,42 @@ func TestSqlQuery(t *testing.T) { // Let's actually start our test sqlDriver, mock := newTestFixtureSQL(t) + query := fmt.Sprintf( + "SELECT %s FROM %s WHERE %s=? AND %s=? AND %s=? AND %s=?", + sqlReleaseTableBodyColumn, + sqlReleaseTableName, + sqlReleaseTableNameColumn, + sqlReleaseTableNamespaceColumn, + sqlReleaseTableOwnerColumn, + sqlReleaseTableStatusColumn, + ) + mock. - ExpectQuery(regexp.QuoteMeta("SELECT body FROM releases WHERE name=? AND owner=? AND status=?")). - WithArgs("smug-pigeon", "helm", "deployed"). + ExpectQuery(regexp.QuoteMeta(query)). + WithArgs("smug-pigeon", namespace, sqlReleaseDefaultOwner, "deployed"). WillReturnRows( mock.NewRows([]string{ - "body", + sqlReleaseTableBodyColumn, }).AddRow( deployedReleaseBody, ), ).RowsWillBeClosed() + query = fmt.Sprintf( + "SELECT %s FROM %s WHERE %s=? AND %s=? AND %s=?", + sqlReleaseTableBodyColumn, + sqlReleaseTableName, + sqlReleaseTableNameColumn, + sqlReleaseTableNamespaceColumn, + sqlReleaseTableOwnerColumn, + ) + mock. - ExpectQuery(regexp.QuoteMeta("SELECT body FROM releases WHERE name=? AND owner=?")). - WithArgs("smug-pigeon", "helm"). + ExpectQuery(regexp.QuoteMeta(query)). + WithArgs("smug-pigeon", namespace, sqlReleaseDefaultOwner). WillReturnRows( mock.NewRows([]string{ - "body", + sqlReleaseTableBodyColumn, }).AddRow( supersededReleaseBody, ).AddRow( @@ -301,7 +411,12 @@ func TestSqlQuery(t *testing.T) { func TestSqlDelete(t *testing.T) { vers := 1 name := "smug-pigeon" - namespace := "default" + + namespace, err := getCurrentNamespace() + if err != nil { + t.Fatalf("could not get namespace: %v", err) + } + key := testKey(name, vers) rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) @@ -309,25 +424,43 @@ func TestSqlDelete(t *testing.T) { sqlDriver, mock := newTestFixtureSQL(t) + selectQuery := fmt.Sprintf( + "SELECT %s FROM %s WHERE %s=$1 AND %s=$2", + sqlReleaseTableBodyColumn, + sqlReleaseTableName, + sqlReleaseTableKeyColumn, + sqlReleaseTableNamespaceColumn, + ) + mock.ExpectBegin() mock. - ExpectQuery("SELECT body FROM releases WHERE key = ?"). - WithArgs(key). + ExpectQuery(regexp.QuoteMeta(selectQuery)). + WithArgs(key, namespace). WillReturnRows( mock.NewRows([]string{ - "body", + sqlReleaseTableBodyColumn, }).AddRow( body, ), ).RowsWillBeClosed() + deleteQuery := fmt.Sprintf( + "DELETE FROM %s WHERE %s = $1 AND %s = $2", + sqlReleaseTableName, + sqlReleaseTableKeyColumn, + sqlReleaseTableNamespaceColumn, + ) + mock. - ExpectExec(regexp.QuoteMeta("DELETE FROM releases WHERE key = $1")). - WithArgs(key). + ExpectExec(regexp.QuoteMeta(deleteQuery)). + WithArgs(key, namespace). WillReturnResult(sqlmock.NewResult(0, 1)) mock.ExpectCommit() deletedRelease, err := sqlDriver.Delete(key) + if err := mock.ExpectationsWereMet(); err != nil { + t.Errorf("sql expectations weren't met: %v", err) + } if err != nil { t.Fatalf("failed to delete release with key %q: %v", key, err) } @@ -335,8 +468,4 @@ func TestSqlDelete(t *testing.T) { if !reflect.DeepEqual(rel, deletedRelease) { t.Errorf("Expected release {%v}, got {%v}", rel, deletedRelease) } - - if err := mock.ExpectationsWereMet(); err != nil { - t.Errorf("sql expectations weren't met: %v", err) - } } diff --git a/pkg/storage/driver/util.go b/pkg/storage/driver/util.go index a87002ab4..9120835d6 100644 --- a/pkg/storage/driver/util.go +++ b/pkg/storage/driver/util.go @@ -23,6 +23,8 @@ import ( "encoding/json" "io/ioutil" + "k8s.io/client-go/tools/clientcmd" + rspb "helm.sh/helm/v3/pkg/release" ) @@ -82,3 +84,17 @@ func decodeRelease(data string) (*rspb.Release, error) { } return &rls, nil } + +// getCurrentNamespace gets the current namespace from the local context +func getCurrentNamespace() (string, error) { + rules := clientcmd.NewDefaultClientConfigLoadingRules() + rules.DefaultClientConfig = &clientcmd.DefaultClientConfig + + cfg := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(rules, &clientcmd.ConfigOverrides{ + ClusterDefaults: clientcmd.ClusterDefaults, + }) + + namespace, _, err := cfg.Namespace() + + return namespace, err +}