/
context.go
150 lines (132 loc) 路 4.76 KB
/
context.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// Copyright 2016-2018, 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.
package plugin
import (
"context"
"io"
"sync"
"github.com/opentracing/opentracing-go"
"google.golang.org/grpc"
"github.com/pulumi/pulumi/sdk/v3/go/common/diag"
"github.com/pulumi/pulumi/sdk/v3/go/common/diag/colors"
"github.com/pulumi/pulumi/sdk/v3/go/common/util/rpcutil"
"github.com/pulumi/pulumi/sdk/v3/go/common/workspace"
)
// Context is used to group related operations together so that
// associated OS resources can be cached, shared, and reclaimed as
// appropriate. It also carries shared plugin configuration.
type Context struct {
Diag diag.Sink // the diagnostics sink to use for messages.
StatusDiag diag.Sink // the diagnostics sink to use for status messages.
Host Host // the host that can be used to fetch providers.
Pwd string // the working directory to spawn all plugins in.
Root string // the root directory of the project.
// If non-nil, configures custom gRPC client options. Receives pluginInfo which is a JSON-serializable bit of
// metadata describing the plugin.
DialOptions func(pluginInfo interface{}) []grpc.DialOption
DebugTraceMutex *sync.Mutex // used internally to syncronize debug tracing
tracingSpan opentracing.Span // the OpenTracing span to parent requests within.
cancelFuncs []context.CancelFunc
baseContext context.Context
}
// NewContext allocates a new context with a given sink and host. Note
// that the host is "owned" by this context from here forwards, such
// that when the context's resources are reclaimed, so too are the
// host's.
func NewContext(d, statusD diag.Sink, host Host, _ ConfigSource,
pwd string, runtimeOptions map[string]interface{}, disableProviderPreview bool,
parentSpan opentracing.Span) (*Context, error) {
// TODO: I think really this ought to just take plugins *workspace.Plugins as an arg, but yaml depends on
// this function so *sigh*. For now just see if there's a project we should be using, and use it if there
// is.
projPath, err := workspace.DetectProjectPath()
var plugins *workspace.Plugins
if err == nil && projPath != "" {
project, err := workspace.LoadProject(projPath)
if err == nil {
plugins = project.Plugins
}
}
root := ""
return NewContextWithRoot(d, statusD, host, pwd, root, runtimeOptions,
disableProviderPreview, parentSpan, plugins)
}
// NewContextWithRoot is a variation of NewContext that also sets known project Root. Additionally accepts Plugins
func NewContextWithRoot(d, statusD diag.Sink, host Host,
pwd, root string, runtimeOptions map[string]interface{}, disableProviderPreview bool,
parentSpan opentracing.Span, plugins *workspace.Plugins) (*Context, error) {
if d == nil {
d = diag.DefaultSink(io.Discard, io.Discard, diag.FormatOptions{Color: colors.Never})
}
if statusD == nil {
statusD = diag.DefaultSink(io.Discard, io.Discard, diag.FormatOptions{Color: colors.Never})
}
ctx := &Context{
Diag: d,
StatusDiag: statusD,
Host: host,
Pwd: pwd,
tracingSpan: parentSpan,
DebugTraceMutex: &sync.Mutex{},
}
if host == nil {
h, err := NewDefaultHost(ctx, runtimeOptions, disableProviderPreview, plugins)
if err != nil {
return nil, err
}
ctx.Host = h
}
return ctx, nil
}
// Request allocates a request sub-context.
func (ctx *Context) Request() context.Context {
c := ctx.baseContext
if c == nil {
c = context.Background()
}
c = opentracing.ContextWithSpan(c, ctx.tracingSpan)
c, cancel := context.WithCancel(c)
ctx.cancelFuncs = append(ctx.cancelFuncs, cancel)
return c
}
// Close reclaims all resources associated with this context.
func (ctx *Context) Close() error {
defer func() {
for _, cancel := range ctx.cancelFuncs {
cancel()
}
}()
if ctx.tracingSpan != nil {
ctx.tracingSpan.Finish()
}
err := ctx.Host.Close()
if err != nil && !rpcutil.IsBenignCloseErr(err) {
return err
}
return nil
}
// WithCancelChannel registers a close channel which will close the returned Context when
// the channel is closed.
//
// WARNING: Calling this function without ever closing `c` will leak go routines.
func (ctx *Context) WithCancelChannel(c <-chan struct{}) *Context {
copy := *ctx
go func() {
select {
case _, _ = <-c:
copy.Close()
}
}()
return ©
}