mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-25 01:35:18 -06:00
346aa964e8
This change brings in support for multiple bundles to be downloaded and activated OPA. This is enabled by using the new config option `bundles` to define the bundles, and deprecates the older `bundle` option. The new `bundles` keyword and structure is propagated through to the decision logs, status API, provenance, stored manifests, etc. Check out the doc changes for all the updated structures. That being said any existing configuration using `bundle` will *not* see the new structure, everything is intended to be backwards compatible (almost to a fault). Fixes: #721 Signed-off-by: Patrick East <east.patrick@gmail.com>
167 lines
5.4 KiB
Go
167 lines
5.4 KiB
Go
// Copyright 2019 The OPA Authors. All rights reserved.
|
|
// Use of this source code is governed by an Apache2
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package bundle
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/open-policy-agent/opa/storage"
|
|
"github.com/open-policy-agent/opa/util"
|
|
)
|
|
|
|
var bundlesBasePath = storage.MustParsePath("/system/bundles")
|
|
|
|
// Note: As needed these helpers could be memoized.
|
|
|
|
// ManifestStoragePath is the storage path used for the given named bundle manifest.
|
|
func ManifestStoragePath(name string) storage.Path {
|
|
return append(bundlesBasePath, name, "manifest")
|
|
}
|
|
|
|
func namedBundlePath(name string) storage.Path {
|
|
return append(bundlesBasePath, name)
|
|
}
|
|
|
|
func rootsPath(name string) storage.Path {
|
|
return append(bundlesBasePath, name, "manifest", "roots")
|
|
}
|
|
|
|
func revisionPath(name string) storage.Path {
|
|
return append(bundlesBasePath, name, "manifest", "revision")
|
|
}
|
|
|
|
// ReadBundleNamesFromStore will return a list of bundle names which have had their metadata stored.
|
|
func ReadBundleNamesFromStore(ctx context.Context, store storage.Store, txn storage.Transaction) ([]string, error) {
|
|
value, err := store.Read(ctx, txn, bundlesBasePath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
bundleMap, ok := value.(map[string]interface{})
|
|
if !ok {
|
|
return nil, fmt.Errorf("corrupt manifest roots")
|
|
}
|
|
|
|
bundles := make([]string, len(bundleMap))
|
|
idx := 0
|
|
for name := range bundleMap {
|
|
bundles[idx] = name
|
|
idx++
|
|
}
|
|
return bundles, nil
|
|
}
|
|
|
|
// WriteManifestToStore will write the manifest into the storage. This function is called when
|
|
// the bundle is activated.
|
|
func WriteManifestToStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string, manifest Manifest) error {
|
|
return write(ctx, store, txn, ManifestStoragePath(name), manifest)
|
|
}
|
|
|
|
func write(ctx context.Context, store storage.Store, txn storage.Transaction, path storage.Path, manifest Manifest) error {
|
|
var value interface{} = manifest
|
|
if err := util.RoundTrip(&value); err != nil {
|
|
return err
|
|
}
|
|
|
|
var dir []string
|
|
if len(path) > 1 {
|
|
dir = path[:len(path)-1]
|
|
}
|
|
|
|
if err := storage.MakeDir(ctx, store, txn, dir); err != nil {
|
|
return err
|
|
}
|
|
|
|
return store.Write(ctx, txn, storage.AddOp, path, value)
|
|
}
|
|
|
|
// EraseManifestFromStore will remove the manifest from storage. This function is called
|
|
// when the bundle is deactivated.
|
|
func EraseManifestFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string) error {
|
|
path := namedBundlePath(name)
|
|
err := store.Write(ctx, txn, storage.RemoveOp, path, nil)
|
|
if err != nil && !storage.IsNotFound(err) {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ReadBundleRootsFromStore returns the roots in the specified bundle.
|
|
// If the bundle is not activated, this function will return
|
|
// storage NotFound error.
|
|
func ReadBundleRootsFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string) ([]string, error) {
|
|
value, err := store.Read(ctx, txn, rootsPath(name))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
sl, ok := value.([]interface{})
|
|
if !ok {
|
|
return nil, fmt.Errorf("corrupt manifest roots")
|
|
}
|
|
|
|
roots := make([]string, len(sl))
|
|
|
|
for i := range sl {
|
|
roots[i], ok = sl[i].(string)
|
|
if !ok {
|
|
return nil, fmt.Errorf("corrupt manifest root")
|
|
}
|
|
}
|
|
|
|
return roots, nil
|
|
}
|
|
|
|
// ReadBundleRevisionFromStore returns the revision in the specified bundle.
|
|
// If the bundle is not activated, this function will return
|
|
// storage NotFound error.
|
|
func ReadBundleRevisionFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string) (string, error) {
|
|
return readRevisionFromStore(ctx, store, txn, revisionPath(name))
|
|
}
|
|
|
|
func readRevisionFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, path storage.Path) (string, error) {
|
|
value, err := store.Read(ctx, txn, path)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
str, ok := value.(string)
|
|
if !ok {
|
|
return "", fmt.Errorf("corrupt manifest revision")
|
|
}
|
|
|
|
return str, nil
|
|
}
|
|
|
|
// Helpers for the older single (unnamed) bundle style manifest storage.
|
|
|
|
// LegacyManifestStoragePath is the older unnamed bundle path for manifests to be stored.
|
|
// Deprecated: Use ManifestStoragePath and named bundles instead.
|
|
var legacyManifestStoragePath = storage.MustParsePath("/system/bundle/manifest")
|
|
var legacyRevisionStoragePath = append(legacyManifestStoragePath, "revision")
|
|
|
|
// LegacyWriteManifestToStore will write the bundle manifest to the older single (unnamed) bundle manifest location.
|
|
// Deprecated: Use WriteManifestToStore and named bundles instead.
|
|
func LegacyWriteManifestToStore(ctx context.Context, store storage.Store, txn storage.Transaction, manifest Manifest) error {
|
|
return write(ctx, store, txn, legacyManifestStoragePath, manifest)
|
|
}
|
|
|
|
// LegacyEraseManifestFromStore will erase the bundle manifest from the older single (unnamed) bundle manifest location.
|
|
// Deprecated: Use WriteManifestToStore and named bundles instead.
|
|
func LegacyEraseManifestFromStore(ctx context.Context, store storage.Store, txn storage.Transaction) error {
|
|
err := store.Write(ctx, txn, storage.RemoveOp, legacyManifestStoragePath, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// LegacyReadRevisionFromStore will read the bundle manifest revision from the older single (unnamed) bundle manifest location.
|
|
// Deprecated: Use ReadBundleRevisionFromStore and named bundles instead.
|
|
func LegacyReadRevisionFromStore(ctx context.Context, store storage.Store, txn storage.Transaction) (string, error) {
|
|
return readRevisionFromStore(ctx, store, txn, legacyRevisionStoragePath)
|
|
}
|