diff --git a/config.go b/config.go index 6ee3524..be85a99 100644 --- a/config.go +++ b/config.go @@ -196,17 +196,17 @@ func (c *Config) OCIRepository(name string) (_ *oci.Repository, err error) { return repo, nil } -// BoltDB constructs and configures a boltdb instance from configuration. +// FileDB constructs and configures a boltdb instance from configuration. // It caches built instances and returns the same instance for subsequent // calls with the same name. -func (c *Config) BoltDB(name string) (*bbolt.DB, error) { +func (c *Config) FileDB(name string) (*bbolt.DB, error) { if db, ok := c.cache.bolt[name]; ok { return db, nil } - conf, ok := c.conf.Sources.Bolt[name] + conf, ok := c.conf.History.File[name] if !ok { - return nil, fmt.Errorf("bolt %q: configuration not found", name) + return nil, fmt.Errorf("file db %q: configuration not found", name) } db, err := bbolt.Open(conf.Path, 0666, nil) diff --git a/docs/_sidebar.md b/docs/_sidebar.md index f6795cc..050b631 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -1,4 +1,4 @@ - [Getting Started](/) - [Concepts](/concepts.md) - [Configuration](/configuration.md) -- [Guides](/guides/index.md) \ No newline at end of file +- [Guides](/guides/index.md) diff --git a/docs/concepts.md b/docs/concepts.md index d2213fe..954a2a3 100644 --- a/docs/concepts.md +++ b/docs/concepts.md @@ -142,6 +142,25 @@ type Edge interface { } ``` +### History + +Pipelines can be configured to record the history of resource state transitions in a target phase. This enables a number of features: + +- Visualization of resource state transitions in the Glu UI. +- Reversion of resource state to a previous version. +- Triggering of promotion based on the presence of a new version (upcoming feature). +- Pinning of resource versions to prevent them from being overwritten (upcoming feature). + +To enable history for a pipeline, you must configure a logging sink for the pipeline. + +We currently support a file-based logger, which writes to a database file on the local filesystem. + +```go +pipeline.LogsTo(pipelines.FileLogger[*SomeResource]("history")) +``` + +File-based history is configured in the [configuration file](./configuration.md). + #### Promotion The core `promotion` kind edge promotes one phase to the next on a call to `Perform(ctx)`. diff --git a/docs/configuration.md b/docs/configuration.md index e9da946..462509a 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -31,6 +31,10 @@ sources: name: origin url: https://github.com/get-glu/example-app credential: github # name of the credential (above) to be used for this source + +history: + file: + path: /tmp/glu-history.db # path to the file on the local filesystem (optional, defaults to a temporary file) ``` Credentials are used to authenticate with the source. [Sources](/?id=sources) allow for viewing and updating of resources (e.g. git repositories, OCI images). @@ -63,7 +67,9 @@ Valid values are `debug`, `info`, `warn`, and `error`. ### credentials -#### `credentials.` +Credentials are used to authenticate with sources. They are referenced by name in the configuration for sources. + +#### credentials.\ The configuration for a named credential. @@ -143,7 +149,11 @@ The path to the private key of the GitHub App. ### sources -#### `sources.` +Sources are used to configure resources (e.g. git repositories, OCI images, files on the local filesystem). + +Different sources have different configuration options and are configured under a key corresponding to the source type. + +#### sources.\ The configuration for a named source. @@ -209,6 +219,24 @@ The reference to the OCI repository. The name of the credential to use for the OCI repository. +### history + +History is used to store the history of the resources and actions performed on them. + +#### `history.type` + +The type of history to use. + +Valid values are `file`. (default) + +#### history.file.\ + +The configuration for a file history. + +#### `history.file..path` + +The path to the file on the local filesystem. + ### server #### `server.port` @@ -232,3 +260,23 @@ The path to the certificate file to use for HTTPS. #### `server.key_file` The path to the key file to use for HTTPS. + +### metrics + +#### `metrics.enabled` + +Whether to enable metrics collection. + +#### `metrics.exporter` + +The exporter to use for metrics collection. Defaults to `prometheus`. + +Valid values are `prometheus` and `otlp`. + +#### `metrics.otlp.endpoint` + +The endpoint to use for OTLP metrics collection. + +#### `metrics.otlp.headers` + +Additional headers to use for OTLP metrics collection. This can be used to add authentication headers, etc. diff --git a/docs/guides/_sidebar.md b/docs/guides/_sidebar.md index 24a3a58..c2ac001 100644 --- a/docs/guides/_sidebar.md +++ b/docs/guides/_sidebar.md @@ -1,3 +1,3 @@ -* Glu and GitOps - * [Overview](/guides/gitops-overview.md) - * [Implementation](/guides/gitops-implementation.md) +- Glu and GitOps + - [Overview](/guides/gitops-overview.md) + - [Implementation](/guides/gitops-implementation.md) diff --git a/docs/guides/gitops-implementation.md b/docs/guides/gitops-implementation.md index 9956e22..152a5b8 100644 --- a/docs/guides/gitops-implementation.md +++ b/docs/guides/gitops-implementation.md @@ -1,5 +1,4 @@ -GitOps and Glu: Implementation -============================== +# GitOps and Glu: Implementation This guide follows on from the previous [GitOps and Glu: Overview](/guides/gitops-overview.md) guide. @@ -412,7 +411,7 @@ func (r *AppResource) WriteTo(ctx context.Context, phase glu.Descriptor, fs fs.F } defer fi.Close() - + // re-encode the deployment and copy it into the file data, err := yaml.Marshal(deployment) if err != nil { diff --git a/docs/guides/gitops-overview.md b/docs/guides/gitops-overview.md index 73c7dda..af08ade 100644 --- a/docs/guides/gitops-overview.md +++ b/docs/guides/gitops-overview.md @@ -1,5 +1,4 @@ -GitOps and Glu: Overview -======================== +# GitOps and Glu: Overview In this guide, we will create a GitOps pipeline for deploying some simple applications to multiple "environments". For the purposes of keeping the illustration simple to create (and destroy), our environments are simply two separate pods in the same cluster. However, Glu can extend to multiple namespaces, clusters, or even non-Kubernetes deployment environments. diff --git a/docs/guides/index.md b/docs/guides/index.md index c3fe852..ffa221e 100644 --- a/docs/guides/index.md +++ b/docs/guides/index.md @@ -1,5 +1,4 @@ -Guides -====== +# Guides This section contains guides for exploring pre-built Glu pipelines in action. diff --git a/examples/oci-and-git/experiment.go b/examples/oci-and-git/experiment.go index 5dba6b4..76c2037 100644 --- a/examples/oci-and-git/experiment.go +++ b/examples/oci-and-git/experiment.go @@ -21,7 +21,7 @@ func run(ctx context.Context) error { system := glu.NewSystem(ctx, glu.Name("mypipelines"), glu.WithUI(ui.FS())) if err := pipelines.NewBuilder(system, glu.Name("checkout"), NewCheckoutResource). // configure a phase logging sink called history - LogsTo(pipelines.BoltLogger[*CheckoutResource]("history")). + LogsTo(pipelines.FileLogger[*CheckoutResource]("history")). // fetch the configured OCI repositority source named "checkout" NewPhase(pipelines.OCIPhase[*CheckoutResource](glu.Name("oci"), "checkout")). // build a phase for the staging environment which source from the git repository diff --git a/pkg/config/config.go b/pkg/config/config.go index 6c4c304..0220745 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -19,12 +19,12 @@ type Config struct { Sources Sources `glu:"sources"` Server Server `glu:"server"` Metrics Metrics `glu:"metrics"` + History History `glu:"history"` } type Sources struct { - Git GitSources `glu:"git"` - OCI OCISources `glu:"oci"` - Bolt BoltDBs `glu:"bolt"` + Git GitSources `glu:"git"` + OCI OCISources `glu:"oci"` } type defaulter interface { diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 32bab42..48571b3 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -232,12 +232,14 @@ func TestConfig(t *testing.T) { }, }, { - path: "testdata/bolt/default", + path: "testdata/history/file/default", expected: &Config{ Log: Log{Level: "info"}, - Sources: Sources{ - Bolt: BoltDBs{ - "default": &BoltDB{ + History: History{ + Type: HistoryTypeFile, + File: FileDBs{ + "default": &FileDB{ + Name: "history", Path: "history.db", }, }, diff --git a/pkg/config/history.go b/pkg/config/history.go index 67ca493..877e792 100644 --- a/pkg/config/history.go +++ b/pkg/config/history.go @@ -6,36 +6,55 @@ import ( "os" ) -type BoltDBs map[string]*BoltDB +type HistoryType string -func (b BoltDBs) validate() error { +const ( + HistoryTypeFile HistoryType = "file" +) + +type History struct { + Type HistoryType `glu:"type"` + File FileDBs `glu:"file"` +} + +func (h *History) setDefaults() error { + if h.Type == "" { + h.Type = HistoryTypeFile + } + + return nil +} + +type FileDBs map[string]*FileDB + +func (b FileDBs) validate() error { for name, source := range b { if err := source.validate(); err != nil { - return fmt.Errorf("bolt %q: %w", name, err) + return fmt.Errorf("file db %q: %w", name, err) } } return nil } -func (b BoltDBs) setDefaults() error { +func (b FileDBs) setDefaults() error { for name, source := range b { if err := source.setDefaults(name); err != nil { - return fmt.Errorf("bolt %q: %w", name, err) + return fmt.Errorf("file db %q: %w", name, err) } } return nil } -type BoltDB struct { +type FileDB struct { Name string `glu:"name"` Path string `glu:"path"` } -func (s *BoltDB) validate() error { +func (s *FileDB) validate() error { if s == nil { - return errFieldRequired("bolt") + return errFieldRequired("file") } if s.Path == "" { @@ -45,7 +64,7 @@ func (s *BoltDB) validate() error { return nil } -func (s *BoltDB) setDefaults(name string) error { +func (s *FileDB) setDefaults(name string) error { if s == nil { return nil } @@ -62,7 +81,7 @@ func (s *BoltDB) setDefaults(name string) error { s.Path = fi.Name() - slog.Info("created temporary file for bolt", "source.bolt", name, "path", s.Path) + slog.Info("created temporary file for file db", "history.file", name, "path", s.Path) } return nil diff --git a/pkg/config/testdata/bolt/default/glu.yml b/pkg/config/testdata/history/file/default/glu.yml similarity index 55% rename from pkg/config/testdata/bolt/default/glu.yml rename to pkg/config/testdata/history/file/default/glu.yml index 235fb70..3fdb588 100644 --- a/pkg/config/testdata/bolt/default/glu.yml +++ b/pkg/config/testdata/history/file/default/glu.yml @@ -1,4 +1,5 @@ -sources: - bolt: +history: + type: file + file: default: path: "history.db" diff --git a/pkg/pipelines/pipelines.go b/pkg/pipelines/pipelines.go index 817003c..e4e3bab 100644 --- a/pkg/pipelines/pipelines.go +++ b/pkg/pipelines/pipelines.go @@ -206,11 +206,11 @@ func OCIPhase[R srcoci.Resource](meta glu.Metadata, srcName string, opts ...cont } } -// BoltLogger returns an instance of type.PhaseLogger which writes to a boltdb file +// FileLogger returns an instance of type.PhaseLogger which writes to a file db // as configured by the provided name. -func BoltLogger[R glu.Resource](name string) func(Builder[R]) (typed.PhaseLogger[R], error) { +func FileLogger[R glu.Resource](name string) func(Builder[R]) (typed.PhaseLogger[R], error) { return func(b Builder[R]) (typed.PhaseLogger[R], error) { - db, err := b.Configuration().BoltDB(name) + db, err := b.Configuration().FileDB(name) if err != nil { return nil, err }