diff --git a/docs/config.md b/docs/config.md index 50ec734ce..a69e30b12 100644 --- a/docs/config.md +++ b/docs/config.md @@ -62,16 +62,17 @@ Supported keys include: ### Storage Configuration -| Key | Description | Supported Values | Default | -|:------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------| -| `storage.gcs.bucket` | The GCS bucket for storage | | | -| `storage.oci.repository` | The OCI repo to store OCI signatures and attestation in | If left undefined _and_ one of `artifacts.{oci,taskrun}.storage` includes `oci` storage, attestations will be stored alongside the stored OCI artifact itself. ([example on GCP](../images/attestations-in-artifact-registry.png)) Defining this value results in the OCI bundle stored in the designated location _instead of_ alongside the image. See [cosign documentation](https://github.com/sigstore/cosign#specifying-registry) for additional information. | | -| `storage.docdb.url` | The go-cloud URI reference to a docstore collection | `firestore://projects/[PROJECT]/databases/(default)/documents/[COLLECTION]?name_field=name` | | -| `storage.docdb.mongo-server-url` (optional) | The value of MONGO_SERVER_URL env var with the MongoDB connection URI | Example: `mongodb://[USER]:[PASSWORD]@[HOST]:[PORT]/[DATABASE]` | | -| `storage.docdb.mongo-server-url-dir` (optional) | The path of the directory that contains the file named MONGO_SERVER_URL that stores the value of MONGO_SERVER_URL env var | If the file `/mnt/mongo-creds-secret/MONGO_SERVER_URL` has the value of MONGO_SERVER_URL, then set `storage.docdb.mongo-server-url-dir: /mnt/mongo-creds-secret` | | -| `storage.grafeas.projectid` | The project of where grafeas server is located for storing occurrences | | | -| `storage.grafeas.noteid` (optional) | This field will be used as the prefix part of the note name that will be created. The value of this field must be a string without spaces. (See more details [below](#grafeas).) | | | -| `storage.grafeas.notehint` (optional) | This field is used to set the [human_readable_name](https://github.com/grafeas/grafeas/blob/cd23d4dc1bef740d6d6d90d5007db5c9a2431c41/proto/v1/attestation.proto#L49) field in the Grafeas ATTESTATION note. If it is not provided, the default `This attestation note was generated by Tekton Chains` will be used. | | | +| Key | Description | Supported Values | Default | +|:-------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------| +| `storage.gcs.bucket` | The GCS bucket for storage | | | +| `storage.oci.repository` | The OCI repo to store OCI signatures and attestation in | If left undefined _and_ one of `artifacts.{oci,taskrun}.storage` includes `oci` storage, attestations will be stored alongside the stored OCI artifact itself. ([example on GCP](../images/attestations-in-artifact-registry.png)) Defining this value results in the OCI bundle stored in the designated location _instead of_ alongside the image. See [cosign documentation](https://github.com/sigstore/cosign#specifying-registry) for additional information. | | +| `storage.docdb.url` | The go-cloud URI reference to a docstore collection | `firestore://projects/[PROJECT]/databases/(default)/documents/[COLLECTION]?name_field=name` | | +| `storage.docdb.mongo-server-url` (optional) | The value of MONGO_SERVER_URL env var with the MongoDB connection URI | Example: `mongodb://[USER]:[PASSWORD]@[HOST]:[PORT]/[DATABASE]` | | +| `storage.docdb.mongo-server-url-dir` (optional) | The path of the directory that contains the file named MONGO_SERVER_URL that stores the value of MONGO_SERVER_URL env var | If the file `/mnt/mongo-creds-secret/MONGO_SERVER_URL` has the value of MONGO_SERVER_URL, then set `storage.docdb.mongo-server-url-dir: /mnt/mongo-creds-secret` | | +| `storage.docdb.mongo-server-url-path` (optional) | The path of the file that contains that stores the value of mongo server url | If the file `/mnt/mongo-creds-secret/mongo-server-url` has the value, then set `storage.docdb.mongo-server-url-path: /mnt/mongo-creds-secret/mongo-server-url` | | +| `storage.grafeas.projectid` | The project of where grafeas server is located for storing occurrences | | | +| `storage.grafeas.noteid` (optional) | This field will be used as the prefix part of the note name that will be created. The value of this field must be a string without spaces. (See more details [below](#grafeas).) | | | +| `storage.grafeas.notehint` (optional) | This field is used to set the [human_readable_name](https://github.com/grafeas/grafeas/blob/cd23d4dc1bef740d6d6d90d5007db5c9a2431c41/proto/v1/attestation.proto#L49) field in the Grafeas ATTESTATION note. If it is not provided, the default `This attestation note was generated by Tekton Chains` will be used. | | | #### docstore @@ -93,6 +94,10 @@ This can be achieved in a few ways: - This field takes precedence over `storage.docdb.mongo-server-url` and `MONGO_SERVER_URL` env var. - The value should point to a directory that has a file named `MONGO_SERVER_URL` that contains the env var. Each time the file is updated, the new value will be read. - One common use case is to store the value of `MONGO_SERVER_URL` in a secret with the key `MONGO_SERVER_URL` and mount the secret at the path specified in this field. When the secret is updated, the new value will be fetched by Tekton Chains. +- Setting the value of `storage.docdb.mongo-server-url-path` field + - This field takes precedence over `storage.docdb.mongo-server-url-dir`, `storage.docdb.mongo-server-url` and `MONGO_SERVER_URL` env var. + - This field should point to the file path that contains the mongo server url. Each time the file is updated, the new value will be read + - One common use case is to store the value of mongo server url in a secret with any key and mount the secret at the path specified. When the secret is updated, the new value will be fetched by Tekton Chains #### Grafeas diff --git a/pkg/chains/storage/docdb/docdb.go b/pkg/chains/storage/docdb/docdb.go index b3fcd0948..ce4db1433 100644 --- a/pkg/chains/storage/docdb/docdb.go +++ b/pkg/chains/storage/docdb/docdb.go @@ -37,6 +37,7 @@ import ( const ( StorageTypeDocDB = "docdb" + mongoEnv = "MONGO_SERVER_URL" ) // ErrNothingToWatch is an error that's returned when the backend doesn't have anything to "watch" @@ -98,34 +99,16 @@ func WatchBackend(ctx context.Context, cfg config.Config, watcherStop chan bool) return nil, ErrNothingToWatch } - // Set up watcher only when `storage.docdb.mongo-server-url-dir` is set - if cfg.Storage.DocDB.MongoServerURLDir == "" { + pathsToWatch := getPathsToWatch(ctx, cfg) + if len(pathsToWatch) == 0 { return nil, ErrNothingToWatch } - logger.Infof("setting up fsnotify watcher for directory: %s", cfg.Storage.DocDB.MongoServerURLDir) - watcher, err := fsnotify.NewWatcher() if err != nil { return nil, err } - pathsToWatch := []string{ - // mongo-server-url-dir/MONGO_SERVER_URL is where the MONGO_SERVER_URL environment - // variable is expected to be mounted, either manually or via a Kubernetes secret, etc. - filepath.Join(cfg.Storage.DocDB.MongoServerURLDir, "MONGO_SERVER_URL"), - // When a Kubernetes secret is mounted on a path, the `data` in that secret is mounted - // under path/..data that is then `symlink`ed to the key of the data. In this instance, - // the mounted path is going to look like: - // file 1 - ..2024_05_03_11_23_23.1253599725 - // file 2 - ..data -> ..2024_05_03_11_23_23.1253599725 - // file 3 - MONGO_SERVER_URL -> ..data/MONGO_SERVER_URL - // So each time the secret is updated, the file `MONGO_SERVER_URL` is not updated, - // instead the underlying symlink at `..data` is updated and that's what we want to - // capture via the fsnotify event watcher - filepath.Join(cfg.Storage.DocDB.MongoServerURLDir, "..data"), - } - backendChan := make(chan *Backend) // Start listening for events. go func() { @@ -145,13 +128,23 @@ func WatchBackend(ctx context.Context, cfg config.Config, watcherStop chan bool) continue } - updatedEnv, err := getMongoServerURLFromDir(cfg.Storage.DocDB.MongoServerURLDir) - if err != nil { - logger.Error(err) - backendChan <- nil + var updatedEnv string + if cfg.Storage.DocDB.MongoServerURLPath != "" { + updatedEnv, err = getMongoServerURLFromPath(cfg.Storage.DocDB.MongoServerURLPath) + if err != nil { + logger.Error(err) + backendChan <- nil + } + } else if cfg.Storage.DocDB.MongoServerURLDir != "" { + updatedEnv, err = getMongoServerURLFromDir(cfg.Storage.DocDB.MongoServerURLDir) + if err != nil { + logger.Error(err) + backendChan <- nil + } } + if updatedEnv != os.Getenv("MONGO_SERVER_URL") { - logger.Infof("directory %s has been updated, reconfiguring backend...", cfg.Storage.DocDB.MongoServerURLDir) + logger.Infof("Mongo server url %s has been updated, reconfiguring backend...", cfg.Storage.DocDB.MongoServerURLPath) // Now that MONGO_SERVER_URL has been updated, we should update docdb backend again newDocDBBackend, err := NewStorageBackend(ctx, cfg) @@ -179,11 +172,21 @@ func WatchBackend(ctx context.Context, cfg config.Config, watcherStop chan bool) } }() - // Add a path. - err = watcher.Add(cfg.Storage.DocDB.MongoServerURLDir) - if err != nil { - return nil, err + if cfg.Storage.DocDB.MongoServerURLPath != "" { + dirPath := filepath.Dir(cfg.Storage.DocDB.MongoServerURLPath) + // Add a path. + err = watcher.Add(dirPath) + if err != nil { + return nil, err + } + } else if cfg.Storage.DocDB.MongoServerURLDir != "" { + // Add a path. + err = watcher.Add(cfg.Storage.DocDB.MongoServerURLDir) + if err != nil { + return nil, err + } } + return backendChan, nil } @@ -260,7 +263,18 @@ func populateMongoServerURL(ctx context.Context, cfg config.Config) error { // If that doesn't exist, then we move on to `storage.docdb.mongo-server-url`. // If that doesn't exist, then we check if `MONGO_SERVER_URL` env var is set. logger := logging.FromContext(ctx) - mongoEnv := "MONGO_SERVER_URL" + + if cfg.Storage.DocDB.MongoServerURLPath != "" { + logger.Infof("setting %s from storage.docdb.mongo-server-url-path: %s", mongoEnv, cfg.Storage.DocDB.MongoServerURLPath) + mongoServerURL, err := getMongoServerURLFromPath(cfg.Storage.DocDB.MongoServerURLPath) + if err != nil { + return err + } + if err := os.Setenv(mongoEnv, mongoServerURL); err != nil { + return err + } + return nil + } if cfg.Storage.DocDB.MongoServerURLDir != "" { logger.Infof("setting %s from storage.docdb.mongo-server-url-dir: %s", mongoEnv, cfg.Storage.DocDB.MongoServerURLDir) @@ -276,7 +290,7 @@ func populateMongoServerURL(ctx context.Context, cfg config.Config) error { if _, envExists := os.LookupEnv(mongoEnv); !envExists { return fmt.Errorf("mongo docstore configured but %s environment variable not set, "+ - "supply one of storage.docdb.mongo-server-url-dir, storage.docdb.mongo-server-url or set %s", mongoEnv, mongoEnv) + "supply one of storage.docdb.mongo-server-url-path, storage.docdb.mongo-server-url-dir, storage.docdb.mongo-server-url or set %s", mongoEnv, mongoEnv) } return nil @@ -288,7 +302,7 @@ func setMongoServerURLFromDir(dir string) error { return err } - if err = os.Setenv("MONGO_SERVER_URL", fileDataNormalized); err != nil { + if err = os.Setenv(mongoEnv, fileDataNormalized); err != nil { return err } @@ -296,7 +310,7 @@ func setMongoServerURLFromDir(dir string) error { } func getMongoServerURLFromDir(dir string) (string, error) { - mongoEnv := "MONGO_SERVER_URL" + // mongoEnv := "MONGO_SERVER_URL" stat, err := os.Stat(dir) if err != nil { @@ -327,3 +341,58 @@ func getMongoServerURLFromDir(dir string) (string, error) { return fileDataNormalized, nil } + +// getMongoServerUrlFromPath retreives token from the given mount path +func getMongoServerURLFromPath(path string) (string, error) { + fileData, err := os.ReadFile(path) + if err != nil { + return "", fmt.Errorf("reading file in %q: %w", path, err) + } + + // A trailing newline is fairly common in mounted files, so remove it. + fileDataNormalized := strings.TrimSuffix(string(fileData), "\n") + return fileDataNormalized, nil +} + +func getPathsToWatch(ctx context.Context, cfg config.Config) []string { + var pathsToWatch = []string{} + logger := logging.FromContext(ctx) + + if cfg.Storage.DocDB.MongoServerURLPath != "" { + logger.Infof("setting up fsnotify watcher for mongo server url path: %s", cfg.Storage.DocDB.MongoServerURLPath) + dirPath := filepath.Dir(cfg.Storage.DocDB.MongoServerURLPath) + pathsToWatch = []string{ + // mongo-server-url-path/ is where the mongo server url token + // When a Kubernetes secret is mounted on a path, the `data` in that secret is mounted + // under path/..data that is then `symlink`ed to the key of the data. In this instance, + // the mounted path is going to look like: + // file 1 - ..2024_05_03_11_23_23.1253599725 + // file 2 - ..data -> ..2024_05_03_11_23_23.1253599725 + // file 3 - ..data/ + // So each time the secret is updated, the file is not updated, + // instead the underlying symlink at `..data` is updated and that's what we want to + // capture via the fsnotify event watcher + // filepath.Join(cfg.Storage.DocDB.MongoServerURLPath, "..data"), + filepath.Join(dirPath, "..data"), + } + } else if cfg.Storage.DocDB.MongoServerURLDir != "" { + logger.Infof("setting up fsnotify watcher for directory: %s", cfg.Storage.DocDB.MongoServerURLDir) + pathsToWatch = []string{ + // mongo-server-url-dir/MONGO_SERVER_URL is where the MONGO_SERVER_URL environment + // variable is expected to be mounted, either manually or via a Kubernetes secret, etc. + filepath.Join(cfg.Storage.DocDB.MongoServerURLDir, "MONGO_SERVER_URL"), + // When a Kubernetes secret is mounted on a path, the `data` in that secret is mounted + // under path/..data that is then `symlink`ed to the key of the data. In this instance, + // the mounted path is going to look like: + // file 1 - ..2024_05_03_11_23_23.1253599725 + // file 2 - ..data -> ..2024_05_03_11_23_23.1253599725 + // file 3 - MONGO_SERVER_URL -> ..data/MONGO_SERVER_URL + // So each time the secret is updated, the file `MONGO_SERVER_URL` is not updated, + // instead the underlying symlink at `..data` is updated and that's what we want to + // capture via the fsnotify event watcher + filepath.Join(cfg.Storage.DocDB.MongoServerURLDir, "..data"), + } + } + + return pathsToWatch +} diff --git a/pkg/config/config.go b/pkg/config/config.go index 7175ac2bc..4e9cef101 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -123,9 +123,10 @@ type TektonStorageConfig struct { } type DocDBStorageConfig struct { - URL string - MongoServerURL string - MongoServerURLDir string + URL string + MongoServerURL string + MongoServerURLDir string + MongoServerURLPath string } type GrafeasConfig struct { @@ -168,12 +169,13 @@ const ( ociStorageKey = "artifacts.oci.storage" ociSignerKey = "artifacts.oci.signer" - gcsBucketKey = "storage.gcs.bucket" - ociRepositoryKey = "storage.oci.repository" - ociRepositoryInsecureKey = "storage.oci.repository.insecure" - docDBUrlKey = "storage.docdb.url" - docDBMongoServerURLKey = "storage.docdb.mongo-server-url" - docDBMongoServerURLDirKey = "storage.docdb.mongo-server-url-dir" + gcsBucketKey = "storage.gcs.bucket" + ociRepositoryKey = "storage.oci.repository" + ociRepositoryInsecureKey = "storage.oci.repository.insecure" + docDBUrlKey = "storage.docdb.url" + docDBMongoServerURLKey = "storage.docdb.mongo-server-url" + docDBMongoServerURLDirKey = "storage.docdb.mongo-server-url-dir" + docDBMongoServerURLPathKey = "storage.docdb.mongo-server-url-path" grafeasProjectIDKey = "storage.grafeas.projectid" grafeasNoteIDKey = "storage.grafeas.noteid" @@ -302,6 +304,7 @@ func NewConfigFromMap(data map[string]string) (*Config, error) { asString(docDBUrlKey, &cfg.Storage.DocDB.URL), asString(docDBMongoServerURLKey, &cfg.Storage.DocDB.MongoServerURL), asString(docDBMongoServerURLDirKey, &cfg.Storage.DocDB.MongoServerURLDir), + asString(docDBMongoServerURLPathKey, &cfg.Storage.DocDB.MongoServerURLPath), asString(grafeasProjectIDKey, &cfg.Storage.Grafeas.ProjectID), asString(grafeasNoteIDKey, &cfg.Storage.Grafeas.NoteID), asString(grafeasNoteHint, &cfg.Storage.Grafeas.NoteHint),