2022-10-11 19:21:07 +00:00
|
|
|
// Copyright 2016-2022, Pulumi Corporation.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
2020-07-07 21:45:18 +00:00
|
|
|
package schema
|
|
|
|
|
|
|
|
import (
|
2022-06-14 06:27:11 +00:00
|
|
|
"bytes"
|
2023-06-28 12:45:57 +00:00
|
|
|
"errors"
|
2021-09-08 21:37:57 +00:00
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"os"
|
2020-07-07 21:45:18 +00:00
|
|
|
"sync"
|
2021-09-08 21:37:57 +00:00
|
|
|
"time"
|
2020-07-07 21:45:18 +00:00
|
|
|
|
2022-06-14 06:27:11 +00:00
|
|
|
"github.com/edsrzf/mmap-go"
|
|
|
|
"github.com/natefinch/atomic"
|
|
|
|
|
2020-07-07 21:45:18 +00:00
|
|
|
"github.com/blang/semver"
|
2022-05-23 22:44:35 +00:00
|
|
|
"github.com/segmentio/encoding/json"
|
2021-11-13 02:37:17 +00:00
|
|
|
|
2023-06-28 12:45:57 +00:00
|
|
|
pkgWorkspace "github.com/pulumi/pulumi/pkg/v3/workspace"
|
2024-04-25 17:30:30 +00:00
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/apitype"
|
2023-06-28 12:45:57 +00:00
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/diag"
|
2023-10-03 15:35:23 +00:00
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/env"
|
2021-03-17 13:20:05 +00:00
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin"
|
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/tokens"
|
2022-01-31 20:48:32 +00:00
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
|
2021-03-17 13:20:05 +00:00
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/workspace"
|
2020-07-07 21:45:18 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Loader interface {
|
|
|
|
LoadPackage(pkg string, version *semver.Version) (*Package, error)
|
|
|
|
}
|
|
|
|
|
2022-05-23 22:44:35 +00:00
|
|
|
type ReferenceLoader interface {
|
|
|
|
Loader
|
|
|
|
|
|
|
|
LoadPackageReference(pkg string, version *semver.Version) (PackageReference, error)
|
|
|
|
}
|
|
|
|
|
2020-07-07 21:45:18 +00:00
|
|
|
type pluginLoader struct {
|
|
|
|
m sync.RWMutex
|
|
|
|
|
|
|
|
host plugin.Host
|
2022-05-23 22:44:35 +00:00
|
|
|
entries map[string]PackageReference
|
2022-06-14 06:27:11 +00:00
|
|
|
|
2022-10-11 10:56:29 +00:00
|
|
|
cacheOptions pluginLoaderCacheOptions
|
2022-06-14 06:27:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Caching options intended for benchmarking or debugging:
|
|
|
|
type pluginLoaderCacheOptions struct {
|
|
|
|
// useEntriesCache enables in-memory re-use of packages
|
|
|
|
disableEntryCache bool
|
|
|
|
// useFileCache enables skipping plugin loading when possible and caching JSON schemas to files
|
|
|
|
disableFileCache bool
|
|
|
|
// useMmap enables the use of memory mapped IO to avoid copying the JSON schema
|
|
|
|
disableMmap bool
|
2020-07-07 21:45:18 +00:00
|
|
|
}
|
|
|
|
|
2022-05-23 22:44:35 +00:00
|
|
|
func NewPluginLoader(host plugin.Host) ReferenceLoader {
|
2020-07-07 21:45:18 +00:00
|
|
|
return &pluginLoader{
|
|
|
|
host: host,
|
2022-05-23 22:44:35 +00:00
|
|
|
entries: map[string]PackageReference{},
|
2020-07-07 21:45:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-14 06:27:11 +00:00
|
|
|
func newPluginLoaderWithOptions(host plugin.Host, cacheOptions pluginLoaderCacheOptions) ReferenceLoader {
|
|
|
|
return &pluginLoader{
|
|
|
|
host: host,
|
|
|
|
entries: map[string]PackageReference{},
|
|
|
|
|
|
|
|
cacheOptions: cacheOptions,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-23 22:44:35 +00:00
|
|
|
func (l *pluginLoader) getPackage(key string) (PackageReference, bool) {
|
2022-06-14 06:27:11 +00:00
|
|
|
if l.cacheOptions.disableEntryCache {
|
|
|
|
return nil, false
|
|
|
|
}
|
2020-07-07 21:45:18 +00:00
|
|
|
p, ok := l.entries[key]
|
|
|
|
return p, ok
|
|
|
|
}
|
|
|
|
|
2022-06-14 06:27:11 +00:00
|
|
|
func (l *pluginLoader) setPackage(key string, p PackageReference) PackageReference {
|
|
|
|
if l.cacheOptions.disableEntryCache {
|
|
|
|
return p
|
|
|
|
}
|
|
|
|
|
|
|
|
if p, ok := l.entries[key]; ok {
|
|
|
|
return p
|
|
|
|
}
|
|
|
|
|
|
|
|
l.entries[key] = p
|
|
|
|
return p
|
|
|
|
}
|
|
|
|
|
2020-07-07 21:45:18 +00:00
|
|
|
func (l *pluginLoader) LoadPackage(pkg string, version *semver.Version) (*Package, error) {
|
2022-05-23 22:44:35 +00:00
|
|
|
ref, err := l.LoadPackageReference(pkg, version)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return ref.Definition()
|
|
|
|
}
|
|
|
|
|
2022-06-01 04:47:34 +00:00
|
|
|
var ErrGetSchemaNotImplemented = getSchemaNotImplemented{}
|
|
|
|
|
|
|
|
type getSchemaNotImplemented struct{}
|
|
|
|
|
|
|
|
func (f getSchemaNotImplemented) Error() string {
|
2023-01-11 15:59:43 +00:00
|
|
|
return "it looks like GetSchema is not implemented"
|
2022-06-01 04:47:34 +00:00
|
|
|
}
|
|
|
|
|
2022-06-02 01:17:06 +00:00
|
|
|
func schemaIsEmpty(schemaBytes []byte) bool {
|
2022-08-19 14:40:59 +00:00
|
|
|
// A non-empty schema is any that contains non-whitespace, non brace characters.
|
|
|
|
//
|
|
|
|
// Some providers implemented GetSchema initially by returning text matching the regular
|
|
|
|
// expression: "\s*\{\s*\}\s*". This handles those cases while not strictly checking that braces
|
|
|
|
// match or reading the whole document.
|
|
|
|
for _, v := range schemaBytes {
|
|
|
|
if v != ' ' && v != '\t' && v != '\r' && v != '\n' && v != '{' && v != '}' {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
2022-06-02 01:17:06 +00:00
|
|
|
}
|
|
|
|
|
2022-05-23 22:44:35 +00:00
|
|
|
func (l *pluginLoader) LoadPackageReference(pkg string, version *semver.Version) (PackageReference, error) {
|
2022-07-19 02:47:31 +00:00
|
|
|
if pkg == "pulumi" {
|
2022-08-08 16:32:27 +00:00
|
|
|
return DefaultPulumiPackage.Reference(), nil
|
2022-07-19 02:47:31 +00:00
|
|
|
}
|
|
|
|
|
2022-06-14 06:27:11 +00:00
|
|
|
l.m.Lock()
|
|
|
|
defer l.m.Unlock()
|
2020-07-07 21:45:18 +00:00
|
|
|
|
2022-06-14 06:27:11 +00:00
|
|
|
key := packageIdentity(pkg, version)
|
2024-03-02 08:15:11 +00:00
|
|
|
if p, ok := l.getPackage(key); ok {
|
2020-07-07 21:45:18 +00:00
|
|
|
return p, nil
|
|
|
|
}
|
|
|
|
|
2022-06-14 06:27:11 +00:00
|
|
|
schemaBytes, version, err := l.loadSchemaBytes(pkg, version)
|
2020-07-07 21:45:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-06-02 01:17:06 +00:00
|
|
|
if schemaIsEmpty(schemaBytes) {
|
2022-06-01 04:47:34 +00:00
|
|
|
return nil, getSchemaNotImplemented{}
|
|
|
|
}
|
2020-07-07 21:45:18 +00:00
|
|
|
|
2022-05-23 22:44:35 +00:00
|
|
|
var spec PartialPackageSpec
|
2022-06-14 06:27:11 +00:00
|
|
|
if _, err := json.Parse(schemaBytes, &spec, json.ZeroCopy); err != nil {
|
2020-07-07 21:45:18 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-07-20 22:12:02 +00:00
|
|
|
// Insert a version into the spec if the package does not provide one or if the
|
|
|
|
// existing version is less than the provided one
|
|
|
|
if version != nil {
|
|
|
|
setVersion := true
|
|
|
|
if spec.PackageInfoSpec.Version != "" {
|
|
|
|
vSemver, err := semver.Make(spec.PackageInfoSpec.Version)
|
|
|
|
if err == nil {
|
|
|
|
if vSemver.Compare(*version) == 1 {
|
|
|
|
setVersion = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if setVersion {
|
|
|
|
spec.PackageInfoSpec.Version = version.String()
|
|
|
|
}
|
2022-05-23 22:44:35 +00:00
|
|
|
}
|
2022-04-30 02:33:48 +00:00
|
|
|
|
Add matrix testing (#13705)
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
Adds the first pass of matrix testing.
Matrix testing allows us to define tests once in pulumi/pulumi via PCL
and then run those tests against each language plugin to verify code
generation and runtime correctness.
Rather than packing matrix tests and all the associated data and
machinery into the CLI itself we define a new Go package at
cmd/pulumi-test-lanaguage. This depends on pkg and runs the deployment
engine in a unique way for matrix tests but it is running the proper
deployment engine with a proper backend (always filestate, using $TEMP).
Currently only NodeJS is hooked up to run these tests, and all the code
for that currently lives in
sdk/nodejs/cmd/pulumi-language-nodejs/language_test.go. I expect we'll
move that helper code to sdk/go/common and use it in each language
plugin to run the tests in the same way.
This first pass includes 3 simple tests:
* l1-empty that runs an empty PCL file and checks just a stack is
created
* l1-output-bool that runs a PCL program that returns two stack outputs
of `true` and `false
* l2-resource-simple that runs a PCL program creating a simple resource
with a single bool property
These tests are themselves tested with a mock language runtime. This
verifies the behavior of the matrix test framework for both correct and
incorrect language hosts (that is some the mock language runtimes
purposefully cause errors or compute the wrong result).
There are a number of things missing from from the core framework still,
but I feel don't block getting this first pass merged and starting to be
used.
1. The tests can not currently run in parallel. That is calling
RunLanguageTest in parallel will break things. This is due to two
separate problems. Firstly is that the SDK snapshot's are not safe to
write in parallel (when PULUMI_ACCEPT is true), this should be fairly
easy to fix by doing a write to dst-{random} and them atomic move to
dst. Secondly is that the deployment engine itself has mutable global
state, short term we should probably just lock around that part
RunLanguageTest, long term it would be good to clean that up.
2. We need a way to verify "preview" behavior, I think this is probably
just a variation of the tests that would call `stack.Preview` and not
pass a snapshot to `assert`.
3. stdout, stderr and log messages are returned in bulk at the end of
the test. Plus there are a couple of calls to the language runtime that
don't correctly thread stdout/stderr to use and so default to the
process `os.Stdout/Stderr`. stdout/stderr streaming shows up in a load
of other places as well so I'm thinking of a clean way to handle all of
them together. Log message streaming we can probably do by just turning
RunLanguageTest to a streaming grpc call.
## Checklist
- [x] I have run `make tidy` to update any new dependencies
- [x] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [x] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
---------
Co-authored-by: Abhinav Gupta <abhinav@pulumi.com>
2023-09-13 15:17:46 +00:00
|
|
|
p, err := ImportPartialSpec(spec, nil, l)
|
2022-05-23 22:44:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2022-04-30 02:33:48 +00:00
|
|
|
}
|
2022-06-14 06:27:11 +00:00
|
|
|
return l.setPackage(key, p), nil
|
2020-07-07 21:45:18 +00:00
|
|
|
}
|
2022-05-23 22:44:35 +00:00
|
|
|
|
|
|
|
func LoadPackageReference(loader Loader, pkg string, version *semver.Version) (PackageReference, error) {
|
2022-11-12 02:16:53 +00:00
|
|
|
var ref PackageReference
|
|
|
|
var err error
|
2022-05-23 22:44:35 +00:00
|
|
|
if refLoader, ok := loader.(ReferenceLoader); ok {
|
2022-11-12 02:16:53 +00:00
|
|
|
ref, err = refLoader.LoadPackageReference(pkg, version)
|
|
|
|
} else {
|
|
|
|
p, pErr := loader.LoadPackage(pkg, version)
|
|
|
|
err = pErr
|
|
|
|
if err == nil {
|
|
|
|
ref = p.Reference()
|
|
|
|
}
|
2022-05-23 22:44:35 +00:00
|
|
|
}
|
2022-11-12 02:16:53 +00:00
|
|
|
|
2022-05-23 22:44:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-11-12 02:16:53 +00:00
|
|
|
|
|
|
|
if pkg != ref.Name() || version != nil && ref.Version() != nil && !ref.Version().Equals(*version) {
|
|
|
|
if l, ok := loader.(*pluginLoader); ok {
|
|
|
|
return nil, fmt.Errorf("req: %s@%v: entries: %v (returned %s@%v)", pkg, version,
|
|
|
|
l.entries, ref.Name(), ref.Version())
|
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("loader returned %s@%v: expected %s@%v", ref.Name(), ref.Version(), pkg, version)
|
|
|
|
}
|
|
|
|
|
|
|
|
return ref, nil
|
2022-05-23 22:44:35 +00:00
|
|
|
}
|
2022-06-14 06:27:11 +00:00
|
|
|
|
|
|
|
func (l *pluginLoader) loadSchemaBytes(pkg string, version *semver.Version) ([]byte, *semver.Version, error) {
|
2024-03-04 21:54:05 +00:00
|
|
|
attachPort, err := plugin.GetProviderAttachPort(tokens.Package(pkg))
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
// If PULUMI_DEBUG_PROVIDERS requested an attach port, skip caching and workspace
|
|
|
|
// interaction and load the schema directly from the given port.
|
|
|
|
if attachPort != nil {
|
|
|
|
schemaBytes, provider, err := l.loadPluginSchemaBytes(pkg, version)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, fmt.Errorf("Error loading schema from plugin: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if version == nil {
|
|
|
|
info, err := provider.GetPluginInfo()
|
|
|
|
contract.IgnoreError(err) // nonfatal error
|
|
|
|
version = info.Version
|
|
|
|
}
|
|
|
|
return schemaBytes, version, nil
|
|
|
|
}
|
|
|
|
|
2024-04-25 17:30:30 +00:00
|
|
|
pluginInfo, err := l.host.ResolvePlugin(apitype.ResourcePlugin, pkg, version)
|
2022-06-14 06:27:11 +00:00
|
|
|
if err != nil {
|
2023-10-03 15:35:23 +00:00
|
|
|
// Try and install the plugin if it was missing and try again, unless auto plugin installs are turned off.
|
|
|
|
if env.DisableAutomaticPluginAcquisition.Value() {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
2023-06-28 12:45:57 +00:00
|
|
|
var missingError *workspace.MissingError
|
|
|
|
if errors.As(err, &missingError) {
|
|
|
|
spec := workspace.PluginSpec{
|
2024-04-25 17:30:30 +00:00
|
|
|
Kind: apitype.ResourcePlugin,
|
2023-06-28 12:45:57 +00:00
|
|
|
Name: pkg,
|
|
|
|
Version: version,
|
|
|
|
}
|
|
|
|
|
|
|
|
log := func(sev diag.Severity, msg string) {
|
|
|
|
l.host.Log(sev, "", msg, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = pkgWorkspace.InstallPlugin(spec, log)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
2024-04-25 17:30:30 +00:00
|
|
|
pluginInfo, err = l.host.ResolvePlugin(apitype.ResourcePlugin, pkg, version)
|
2023-06-28 12:45:57 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, version, err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
2022-06-14 06:27:11 +00:00
|
|
|
}
|
2022-09-08 16:45:15 +00:00
|
|
|
contract.Assertf(pluginInfo != nil, "loading pkg %q: pluginInfo was unexpectedly nil", pkg)
|
2022-06-14 06:27:11 +00:00
|
|
|
|
|
|
|
if version == nil {
|
|
|
|
version = pluginInfo.Version
|
|
|
|
}
|
|
|
|
|
2022-10-07 23:05:45 +00:00
|
|
|
if pluginInfo.SchemaPath != "" && version != nil {
|
2022-06-14 06:27:11 +00:00
|
|
|
schemaBytes, ok := l.loadCachedSchemaBytes(pkg, pluginInfo.SchemaPath, pluginInfo.SchemaTime)
|
|
|
|
if ok {
|
|
|
|
return schemaBytes, nil, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
schemaBytes, provider, err := l.loadPluginSchemaBytes(pkg, version)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, fmt.Errorf("Error loading schema from plugin: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if pluginInfo.SchemaPath != "" {
|
|
|
|
err = atomic.WriteFile(pluginInfo.SchemaPath, bytes.NewReader(schemaBytes))
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, fmt.Errorf("Error writing schema from plugin to cache: %w", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if version == nil {
|
golangci-lint: Enable staticcheck
Remove staticcheck from the list of disabled linters.
It's enabled by default in golangci-lint.
This also fixes minor remaining staticcheck issues
that don't merit their own pull requests,
or opts out of those that cannot be fixed yet.
Notably, we're opting out of:
- Resource.Name is deprecated (#9469)
- github.com/golang/protobuf is deprecated (#11869)
- strings.Title has been deprecated (#11870)
Besides that, other issues addressed in this change are:
```
// all issues are in pkg
codegen/schema/docs_parser.go:103:4: SA4006: this value of `text` is never used (staticcheck)
codegen/schema/loader.go:253:3: SA9003: empty branch (staticcheck)
resource/deploy/step_executor.go:328:12: SA9003: empty branch (staticcheck)
resource/deploy/step_generator.go:141:10: SA9003: empty branch (staticcheck)
codegen/pcl/invoke.go:97:10: SA9003: empty branch (staticcheck)
codegen/hcl2/model/type_const.go:57:2: SA9003: empty branch (staticcheck)
codegen/hcl2/model/type_enum.go:99:9: SA4001: &*x will be simplified to x. It will not copy x. (staticcheck)
codegen/go/gen_test.go:399:19: SA4017: HasPrefix is a pure function but its return value is ignored (staticcheck)
```
Depends on #11857, #11858, #11859, #11860, #11862, #11865, #11866, #11867, #11868
Resolves #11808
2023-01-11 19:53:41 +00:00
|
|
|
info, _ := provider.GetPluginInfo() // nonfatal error
|
2022-06-14 06:27:11 +00:00
|
|
|
version = info.Version
|
|
|
|
}
|
|
|
|
|
|
|
|
return schemaBytes, version, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *pluginLoader) loadPluginSchemaBytes(pkg string, version *semver.Version) ([]byte, plugin.Provider, error) {
|
|
|
|
provider, err := l.host.Provider(tokens.Package(pkg), version)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
2023-02-17 01:23:09 +00:00
|
|
|
contract.Assertf(provider != nil, "unexpected nil provider for %s@%v", pkg, version)
|
2022-06-14 06:27:11 +00:00
|
|
|
|
|
|
|
schemaFormatVersion := 0
|
|
|
|
schemaBytes, err := provider.GetSchema(schemaFormatVersion)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return schemaBytes, provider, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var mmapedFiles = make(map[string]mmap.MMap)
|
|
|
|
|
|
|
|
func (l *pluginLoader) loadCachedSchemaBytes(pkg string, path string, schemaTime time.Time) ([]byte, bool) {
|
|
|
|
if l.cacheOptions.disableFileCache {
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
|
|
|
|
if schemaMmap, ok := mmapedFiles[path]; ok {
|
|
|
|
return schemaMmap, true
|
|
|
|
}
|
|
|
|
|
|
|
|
success := false
|
2023-03-03 16:36:39 +00:00
|
|
|
schemaFile, err := os.OpenFile(path, os.O_RDONLY, 0o644)
|
2022-06-14 06:27:11 +00:00
|
|
|
defer func() {
|
|
|
|
if !success {
|
|
|
|
schemaFile.Close()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
if err != nil {
|
|
|
|
return nil, success
|
|
|
|
}
|
|
|
|
|
|
|
|
stat, err := schemaFile.Stat()
|
|
|
|
if err != nil {
|
|
|
|
return nil, success
|
|
|
|
}
|
|
|
|
cachedAt := stat.ModTime()
|
|
|
|
|
|
|
|
if schemaTime.After(cachedAt) {
|
|
|
|
return nil, success
|
|
|
|
}
|
|
|
|
|
|
|
|
if l.cacheOptions.disableMmap {
|
|
|
|
data, err := io.ReadAll(schemaFile)
|
|
|
|
if err != nil {
|
|
|
|
return nil, success
|
|
|
|
}
|
|
|
|
success = true
|
|
|
|
return data, success
|
|
|
|
}
|
|
|
|
|
|
|
|
schemaMmap, err := mmap.Map(schemaFile, mmap.RDONLY, 0)
|
|
|
|
if err != nil {
|
|
|
|
return nil, success
|
|
|
|
}
|
|
|
|
success = true
|
|
|
|
|
|
|
|
return schemaMmap, success
|
|
|
|
}
|