// Copyright 2020 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" "errors" "fmt" "io" "os" "path/filepath" "github.com/open-policy-agent/opa/v1/ast" "github.com/open-policy-agent/opa/v1/bundle" "github.com/open-policy-agent/opa/v1/resolver/wasm" "github.com/open-policy-agent/opa/v1/storage" ) // LoadWasmResolversFromStore will lookup all Wasm modules from the store along with the // associated bundle manifest configuration and instantiate the respective resolvers. func LoadWasmResolversFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, otherBundles map[string]*bundle.Bundle) ([]*wasm.Resolver, error) { bundleNames, err := bundle.ReadBundleNamesFromStore(ctx, store, txn) if err != nil && !storage.IsNotFound(err) { return nil, err } var resolversToLoad []*bundle.WasmModuleFile for _, bundleName := range bundleNames { var wasmResolverConfigs []bundle.WasmResolver rawModules := map[string][]byte{} // Save round-tripping the bundle that was just activated if _, ok := otherBundles[bundleName]; ok { wasmResolverConfigs = otherBundles[bundleName].Manifest.WasmResolvers for _, wmf := range otherBundles[bundleName].WasmModules { rawModules[wmf.Path] = wmf.Raw } } else { wasmResolverConfigs, err = bundle.ReadWasmMetadataFromStore(ctx, store, txn, bundleName) if err != nil && !storage.IsNotFound(err) { return nil, fmt.Errorf("failed to read wasm module manifest from store: %s", err) } rawModules, err = bundle.ReadWasmModulesFromStore(ctx, store, txn, bundleName) if err != nil && !storage.IsNotFound(err) { return nil, fmt.Errorf("failed to read wasm modules from store: %s", err) } } for path, raw := range rawModules { wmf := &bundle.WasmModuleFile{ URL: path, Path: path, Raw: raw, } for _, resolverConf := range wasmResolverConfigs { if resolverConf.Module == path { ref, err := ast.PtrRef(ast.DefaultRootDocument, resolverConf.Entrypoint) if err != nil { return nil, fmt.Errorf("failed to parse wasm module entrypoint '%s': %s", resolverConf.Entrypoint, err) } wmf.Entrypoints = append(wmf.Entrypoints, ref) } } if len(wmf.Entrypoints) > 0 { resolversToLoad = append(resolversToLoad, wmf) } } } var resolvers []*wasm.Resolver if len(resolversToLoad) > 0 { // Get a full snapshot of the current data (including any from "outside" the bundles) data, err := store.Read(ctx, txn, storage.Path{}) if err != nil { return nil, fmt.Errorf("failed to initialize wasm runtime: %s", err) } for _, wmf := range resolversToLoad { resolver, err := wasm.New(wmf.Entrypoints, wmf.Raw, data) if err != nil { return nil, fmt.Errorf("failed to initialize wasm module for entrypoints '%s': %s", wmf.Entrypoints, err) } resolvers = append(resolvers, resolver) } } return resolvers, nil } // LoadBundleFromDisk loads a previously persisted activated bundle from disk func LoadBundleFromDisk(path, name string, bvc *bundle.VerificationConfig) (*bundle.Bundle, error) { return LoadBundleFromDiskForRegoVersion(ast.RegoV0, path, name, bvc) } func LoadBundleFromDiskForRegoVersion(regoVersion ast.RegoVersion, path, name string, bvc *bundle.VerificationConfig) (*bundle.Bundle, error) { bundlePath := filepath.Join(path, name, "bundle.tar.gz") _, err := os.Stat(bundlePath) if err == nil { f, err := os.Open(bundlePath) if err != nil { return nil, err } defer f.Close() r := bundle.NewCustomReader(bundle.NewTarballLoaderWithBaseURL(f, "")). WithRegoVersion(regoVersion) if bvc != nil { r = r.WithBundleVerificationConfig(bvc) } b, err := r.Read() if err != nil { return nil, err } return &b, nil } else if os.IsNotExist(err) { return nil, nil } return nil, err } // SaveBundleToDisk saves the given raw bytes representing the bundle's content to disk func SaveBundleToDisk(path string, raw io.Reader) (string, error) { if _, err := os.Stat(path); os.IsNotExist(err) { err = os.MkdirAll(path, os.ModePerm) if err != nil { return "", err } } if raw == nil { return "", errors.New("no raw bundle bytes to persist to disk") } dest, err := os.CreateTemp(path, ".bundle.tar.gz.*.tmp") if err != nil { return "", err } defer dest.Close() _, err = io.Copy(dest, raw) return dest.Name(), err }