From 69a8f5fa5d47faa1d64d682ec184a45ade8ed192 Mon Sep 17 00:00:00 2001 From: Puneet Punamiya Date: Thu, 10 Oct 2024 18:52:10 +0530 Subject: [PATCH] Support mongo server url path (#1213) Previously when `storage.docdb.mongo-server-url-dir` was set, then value was read from the path specified in the field plus `MONGO_SERVER_URL` i.e. (for e.g. /mnt/mongo-creds-secret/MONGO_SERVER_URL) so user had to create the secret with the key `MONGO_SERVER_URL` Hence, with this patch user can specify the path now and the tokwn will be read from the path specified Signed-off-by: PuneetPunamiya --- docs/config.md | 51 ++++++---- pkg/chains/storage/docdb/docdb.go | 150 +++++++++++++++++++++++------- pkg/config/config.go | 21 +++-- 3 files changed, 158 insertions(+), 64 deletions(-) diff --git a/docs/config.md b/docs/config.md index 50ec734ced..fc63ee090f 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 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 @@ -83,16 +84,28 @@ You can read about the go-cloud docstore URI format [here](https://gocloud.dev/h #### MongoDB -With MongoDB you will need to supply the value of `MONGO_SERVER_URL` env var with the MongoDB connection URI to the Tekton Chains, the go-cloud URI is just to point at the db and collection. -This can be achieved in a few ways: +You can provide MongoDB connection through different options -- Setting the `MONGO_SERVER_URL` env var in the `tekton-chains-controller` deployment. -- Setting the value of `storage.docdb.mongo-server-url` field. - - This field takes precedence over the `MONGO_SERVER_URL` env var. -- Setting the value of `storage.docdb.mongo-server-url-dir` field. - - 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. +* Using MONGO_SERVER_URL Environment Variable + * User can set the MongoDB connection URL in the MONGO_SERVER_URL env var in the Chains deployment + +* Using `storage.docdb.mongo-server-url` field in the chains-config configmap + * Alternatively, you can set the connection URL using the `storage.docdb.mongo-server-url` field in the chains-config configmap + * This field overrides the MONGO_SERVER_URL env var + +* Using `storage.docdb.mongo-server-url-dir` field + * Another option is to set `storage.docdb.mongo-server-url-dir`, which points to a directory containing a file named `MONGO_SERVER_URL` + * The directory path setting takes precedence over both `storage.docdb.mongo-server-url` and the `MONGO_SERVER_URL` env var + * For instance, if `/mnt/mongo-creds-secret/MONGO_SERVER_URL` contains the MongoDB URL, set `storage.docdb.mongo-server-url-dir`: `/mnt/mongo-creds-secret` + +* Using `storage.docdb.mongo-server-url-path` field + * You can use `storage.docdb.mongo-server-url-path` field in chains-config configmap to directly reference the file containing the MongoDB URL + * This field overrides all others (`mongo-server-url-dir, mongo-server-url, and MONGO_SERVER_URL env var`) + * For instance, if `/mnt/mongo-creds-secret/mongo-server-url` contains the MongoDB URL, then set `storage.docdb.mongo-server-url-path`: `/mnt/mongo-creds-secret/mongo-server-url` + +**NOTE** :- +* When using `storage.docdb.mongo-server-url-dir` or `storage.docdb.mongo-server-url-path` field, store the value of mongo server url in a secret and mount the secret. When the secret is updated, the new value will be fetched by Tekton Chains controller +* Also using `storage.docdb.mongo-server-url-dir` or `storage.docdb.mongo-server-url-path` field are recommended, using `storage.docdb.mongo-server-url` should be avoided since credentials are stored in a ConfigMap instead of a secret #### Grafeas diff --git a/pkg/chains/storage/docdb/docdb.go b/pkg/chains/storage/docdb/docdb.go index b3fcd09483..d732641661 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.Info("Mongo server url has been updated, reconfiguring backend...") // Now that MONGO_SERVER_URL has been updated, we should update docdb backend again newDocDBBackend, err := NewStorageBackend(ctx, cfg) @@ -179,11 +172,23 @@ 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 + } } + + if cfg.Storage.DocDB.MongoServerURLDir != "" { + // Add a path. + err = watcher.Add(cfg.Storage.DocDB.MongoServerURLDir) + if err != nil { + return nil, err + } + } + return backendChan, nil } @@ -256,27 +261,43 @@ func (b *Backend) retrieveDocuments(ctx context.Context, opts config.StorageOpts } func populateMongoServerURL(ctx context.Context, cfg config.Config) error { - // First preference is given to the key `storage.docdb.mongo-server-url-dir`. + // First preference is given to the key `storage.docdb.mongo-server-url-path`. + // If that doesn't exist, then we move on to `storage.docdb.mongo-server-url-dir`. // 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) if err := setMongoServerURLFromDir(cfg.Storage.DocDB.MongoServerURLDir); err != nil { return err } - } else if cfg.Storage.DocDB.MongoServerURL != "" { + return nil + } + + if cfg.Storage.DocDB.MongoServerURL != "" { logger.Infof("setting %s from storage.docdb.mongo-server-url", mongoEnv) if err := os.Setenv(mongoEnv, cfg.Storage.DocDB.MongoServerURL); err != nil { return err } + return nil } 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 +309,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,8 +317,6 @@ func setMongoServerURLFromDir(dir string) error { } func getMongoServerURLFromDir(dir string) (string, error) { - mongoEnv := "MONGO_SERVER_URL" - stat, err := os.Stat(dir) if err != nil { if os.IsNotExist(err) { @@ -327,3 +346,62 @@ 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"), + } + return pathsToWatch + } + + 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 + } + + return pathsToWatch +} diff --git a/pkg/config/config.go b/pkg/config/config.go index 7175ac2bc1..4e9cef101a 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),