Skip to content
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

Namespaces #1459

Merged
merged 48 commits into from
Apr 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
b3d7489
feat: (sqlite) support namespace at storage level, mostly for flags (…
markphelps Mar 1, 2023
27df88a
feat: Namespaces segments storage (#1369)
markphelps Mar 2, 2023
d8482a3
feat: (sqlite) add namespace support for rules storage (#1371)
markphelps Mar 3, 2023
37062fb
feat: (sqlite) eval storage namespace support (#1372)
markphelps Mar 3, 2023
5feec90
Merge branch 'main' into namespaces
markphelps Mar 6, 2023
9e1afb7
Namespaces storage (#1383)
markphelps Mar 8, 2023
3f22664
Namespaces mysql (#1386)
markphelps Mar 8, 2023
a9941c5
Namespaces postgres (#1388)
markphelps Mar 9, 2023
f80825b
Namespaces cockroach (#1390)
markphelps Mar 13, 2023
419d919
chore: namespaces down migrations (#1396)
markphelps Mar 14, 2023
6c24e36
chore: merge/proto
markphelps Mar 15, 2023
5f9d4d1
Namespaces: flags storage tests (#1406)
markphelps Mar 16, 2023
9b075ea
Namespaces: segments storage tests (#1408)
markphelps Mar 17, 2023
1d77660
chore: add remaining storage layer tests (#1412)
markphelps Mar 20, 2023
cac4042
feat: add namespaces server mappings (#1415)
markphelps Mar 20, 2023
d98dee3
chore: merge main; regen protos
markphelps Mar 20, 2023
bdbe9be
Namespaces rpc (#1421)
markphelps Mar 20, 2023
1f9b339
feat: check for protected or flags existing when deleting a namespace…
markphelps Mar 21, 2023
5d9769f
Merge branch 'main' into namespaces
GeorgeMac Mar 23, 2023
45d34a2
feat(hack/build): add cases for namespace scoped integration tests (#…
GeorgeMac Mar 23, 2023
0d57382
chore: regen protos; merge main
markphelps Mar 24, 2023
6a0906a
Merge branch 'main' into namespaces
markphelps Mar 27, 2023
6d4def3
feat: add check for rule namespaced errors
yquansah Mar 27, 2023
68aa861
fix: namespace segment REST API routes were not correct (#1445)
markphelps Mar 28, 2023
76f4034
feat: add distribution test for cross-namespace entities
yquansah Mar 28, 2023
4460c99
feat: leave namespace check to distribution
yquansah Mar 28, 2023
bd06156
feat: generate namespaced scoped routes for distributions
yquansah Mar 28, 2023
5ca6d24
chore: address changes in regards to error messaging
yquansah Mar 28, 2023
75cb62c
chore: abstract out the two types of DistributionRequests
yquansah Mar 29, 2023
4d87982
fix(migrations/sqlite): reorder to ensure we copy all data before dro…
GeorgeMac Mar 29, 2023
d9500dc
chore: do joins on the query and make error messages match
yquansah Mar 29, 2023
9578d51
Merge pull request #1444 from flipt-io/yq-rule-errors
yquansah Mar 29, 2023
6ae493c
chore: merge main into namespaces (#1448)
GeorgeMac Mar 30, 2023
ac89b78
chore: add missing namespace metrics (#1450)
markphelps Mar 30, 2023
1f36e55
chore: allow testing of namespaces ui branch for build
markphelps Mar 30, 2023
b3d1e8e
chore: rm down migrations (#1449)
markphelps Mar 30, 2023
641d69d
feat(cmd): import and export namespace flag (#1452)
GeorgeMac Mar 31, 2023
64caca0
fix(cmd/import): pass namespace key as name on create
GeorgeMac Apr 3, 2023
c16b774
fix(hack/build): assert returned namespace name is as expected
GeorgeMac Apr 3, 2023
15c9a9f
chore: validation logic for namespace requests (#1457)
yquansah Apr 3, 2023
a528a6a
Merge branch 'namespaces' into gm/import-create-namespace-name
GeorgeMac Apr 3, 2023
3d92307
Merge pull request #1456 from flipt-io/gm/import-create-namespace-name
GeorgeMac Apr 3, 2023
a0c4efb
Merge branch 'main' into namespaces
GeorgeMac Apr 5, 2023
878bd86
chore: rm TODO comments in exporter
markphelps Apr 5, 2023
73e6a37
chore: mage proto
markphelps Apr 5, 2023
3273c67
chore: update changelog for future namespace release; add mage proto …
markphelps Apr 5, 2023
7776551
chore: trigger lint build
markphelps Apr 5, 2023
5218a4b
chore: rm mage proto check for now as its not working
markphelps Apr 5, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,4 @@ jobs:
run: go mod tidy

- name: Ensure clean git state.
run: git diff-index --quiet HEAD -- || (echo "Please run go mod tidy." && exit 1)
run: git diff-index --quiet HEAD -- || (echo "Please run 'go mod tidy' and commit changes." && exit 1)
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@
This format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

### Added

- Support for 'namespacing' / multi-environments. All types can now belong to a namespace allowing you to seperate your flags/segments/etc.

### Changed

- All existing objects have been moved to the 'default' namespace to be fully backward compatible.
- Import/Export have been updated to be 'namespace-aware'
- Dependency updates

## [v1.19.3](https://github.com/flipt-io/flipt/releases/tag/v1.19.3) - 2023-03-22

### Changed
Expand Down
119 changes: 73 additions & 46 deletions cmd/flipt/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,73 +5,100 @@ import (
"fmt"
"io"
"os"
"os/signal"
"syscall"
"time"

"github.com/spf13/cobra"
"go.flipt.io/flipt/internal/ext"
"go.flipt.io/flipt/internal/storage"
"go.flipt.io/flipt/internal/storage/sql"
"go.flipt.io/flipt/internal/storage/sql/mysql"
"go.flipt.io/flipt/internal/storage/sql/postgres"
"go.flipt.io/flipt/internal/storage/sql/sqlite"
"go.uber.org/zap"
)

var exportFilename string

func runExport(ctx context.Context, logger *zap.Logger) error {
ctx, cancel := context.WithCancel(ctx)

defer cancel()

interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM)
type exportCommand struct {
filename string
address string
token string
namespace string
}

go func() {
<-interrupt
cancel()
}()
func newExportCommand() *cobra.Command {
export := &exportCommand{}

db, driver, err := sql.Open(*cfg)
if err != nil {
return fmt.Errorf("opening db: %w", err)
cmd := &cobra.Command{
Use: "export",
Short: "Export flags/segments/rules to file/stdout",
RunE: export.run,
}

defer db.Close()

var store storage.Store

switch driver {
case sql.SQLite:
store = sqlite.NewStore(db, logger)
case sql.Postgres, sql.CockroachDB:
store = postgres.NewStore(db, logger)
case sql.MySQL:
store = mysql.NewStore(db, logger)
}
cmd.Flags().StringVarP(
&export.filename,
"output", "o",
"",
"export to filename (default STDOUT)",
)

cmd.Flags().StringVarP(
&export.address,
"address", "a",
"",
"address of remote Flipt instance to export from (defaults to direct DB export if not supplied)",
)

cmd.Flags().StringVarP(
&export.token,
"token", "t",
"",
"client token used to authenticate access to remote Flipt instance when exporting.",
)

cmd.Flags().StringVarP(
&export.namespace,
"namespace", "n",
"default",
"source namespace for exported resources.",
)

return cmd
}

// default to stdout
var out io.WriteCloser = os.Stdout
func (c *exportCommand) run(cmd *cobra.Command, _ []string) error {
var (
// default to stdout
out io.Writer = os.Stdout
logger = zap.Must(zap.NewDevelopment())
)

// export to file
if exportFilename != "" {
logger.Debug("exporting", zap.String("destination_path", exportFilename))
if c.filename != "" {
logger.Debug("exporting", zap.String("destination_path", c.filename))

out, err = os.Create(exportFilename)
fi, err := os.Create(c.filename)
if err != nil {
return fmt.Errorf("creating output file: %w", err)
}

fmt.Fprintf(out, "# exported by Flipt (%s) on %s\n\n", version, time.Now().UTC().Format(time.RFC3339))
defer fi.Close()

fmt.Fprintf(fi, "# exported by Flipt (%s) on %s\n\n", version, time.Now().UTC().Format(time.RFC3339))

out = fi
}

defer out.Close()
// Use client when remote address is configured.
if c.address != "" {
return c.export(cmd.Context(), out, fliptClient(logger, c.address, c.token))
}

exporter := ext.NewExporter(store)
if err := exporter.Export(ctx, out); err != nil {
return fmt.Errorf("exporting: %w", err)
// Otherwise, go direct to the DB using Flipt configuration file.
logger, cfg := buildConfig()
server, cleanup, err := fliptServer(logger, cfg)
if err != nil {
return err
}

return nil
defer cleanup()

return c.export(cmd.Context(), out, server)
}

func (c *exportCommand) export(ctx context.Context, dst io.Writer, lister ext.Lister) error {
return ext.NewExporter(lister, c.namespace).Export(ctx, dst)
}
141 changes: 93 additions & 48 deletions cmd/flipt/import.go
Original file line number Diff line number Diff line change
@@ -1,63 +1,88 @@
package main

import (
"context"
"errors"
"fmt"
"io"
"os"
"os/signal"
"path/filepath"
"syscall"

"github.com/spf13/cobra"
"go.flipt.io/flipt/internal/ext"
"go.flipt.io/flipt/internal/storage"
"go.flipt.io/flipt/internal/storage/sql"
"go.flipt.io/flipt/internal/storage/sql/mysql"
"go.flipt.io/flipt/internal/storage/sql/postgres"
"go.flipt.io/flipt/internal/storage/sql/sqlite"
"go.uber.org/zap"
)

var (
type importCommand struct {
dropBeforeImport bool
importStdin bool
)

func runImport(ctx context.Context, logger *zap.Logger, args []string) error {
ctx, cancel := context.WithCancel(ctx)

defer cancel()

interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM)
address string
token string
namespace string
createNamespace bool
}

go func() {
<-interrupt
cancel()
}()
func newImportCommand() *cobra.Command {
importCmd := &importCommand{}

db, driver, err := sql.Open(*cfg)
if err != nil {
return fmt.Errorf("opening db: %w", err)
cmd := &cobra.Command{
Use: "import",
Short: "Import flags/segments/rules from file",
RunE: importCmd.run,
}

defer db.Close()

var store storage.Store

switch driver {
case sql.SQLite:
store = sqlite.NewStore(db, logger)
case sql.Postgres, sql.CockroachDB:
store = postgres.NewStore(db, logger)
case sql.MySQL:
store = mysql.NewStore(db, logger)
}
cmd.Flags().BoolVar(
&importCmd.dropBeforeImport,
"drop",
false,
"drop database before import",
)

cmd.Flags().BoolVar(
&importCmd.importStdin,
"stdin",
false,
"import from STDIN",
)

cmd.Flags().StringVarP(
&importCmd.address,
"address", "a",
"",
"address of remote Flipt instance to import into (defaults to direct DB import if not supplied)",
)

cmd.Flags().StringVarP(
&importCmd.token,
"token", "t",
"",
"client token used to authenticate access to remote Flipt instance when importing.",
)

cmd.Flags().StringVarP(
&importCmd.namespace,
"namespace", "n",
"default",
"destination namespace for imported resources.",
)

cmd.Flags().BoolVar(
&importCmd.createNamespace,
"create-namespace",
false,
"create the namespace if it does not exist.",
)

return cmd
}

var in io.ReadCloser = os.Stdin
func (c *importCommand) run(cmd *cobra.Command, args []string) error {
var (
in io.Reader = os.Stdin
logger = zap.Must(zap.NewDevelopment())
)

if !importStdin {
if !c.importStdin {
importFilename := args[0]
if importFilename == "" {
return errors.New("import filename required")
Expand All @@ -67,13 +92,26 @@ func runImport(ctx context.Context, logger *zap.Logger, args []string) error {

logger.Debug("importing", zap.String("source_path", f))

in, err = os.Open(f)
fi, err := os.Open(f)
if err != nil {
return fmt.Errorf("opening import file: %w", err)
}

defer fi.Close()

in = fi
}

// Use client when remote address is configured.
if c.address != "" {
return ext.NewImporter(
fliptClient(logger, c.address, c.token),
c.namespace,
c.createNamespace,
).Import(cmd.Context(), in)
}

defer in.Close()
logger, cfg := buildConfig()

migrator, err := sql.NewMigrator(*cfg, logger)
if err != nil {
Expand All @@ -83,11 +121,11 @@ func runImport(ctx context.Context, logger *zap.Logger, args []string) error {
defer migrator.Close()

// drop tables if specified
if dropBeforeImport {
logger.Debug("dropping tables before import")
if c.dropBeforeImport {
logger.Debug("dropping tables")

if err := migrator.Down(); err != nil {
return fmt.Errorf("attempting to drop during import: %w", err)
if err := migrator.Drop(); err != nil {
return fmt.Errorf("attempting to drop: %w", err)
}
}

Expand All @@ -99,10 +137,17 @@ func runImport(ctx context.Context, logger *zap.Logger, args []string) error {
return fmt.Errorf("closing migrator: %w", err)
}

importer := ext.NewImporter(store)
if err := importer.Import(ctx, in); err != nil {
return fmt.Errorf("importing: %w", err)
// Otherwise, go direct to the DB using Flipt configuration file.
server, cleanup, err := fliptServer(logger, cfg)
if err != nil {
return err
}

return nil
defer cleanup()

return ext.NewImporter(
server,
c.namespace,
c.createNamespace,
).Import(cmd.Context(), in)
}
Loading