4
4
"context"
5
5
"errors"
6
6
"fmt"
7
- "net/url"
8
7
"strings"
9
8
"sync"
10
9
@@ -28,6 +27,7 @@ import (
28
27
"get.porter.sh/porter/pkg/storage/migrations"
29
28
storageplugin "get.porter.sh/porter/pkg/storage/pluginstore"
30
29
"get.porter.sh/porter/pkg/storage/sql"
30
+ "get.porter.sh/porter/pkg/storage/sql/migrate"
31
31
"get.porter.sh/porter/pkg/templates"
32
32
"get.porter.sh/porter/pkg/tracing"
33
33
)
@@ -54,40 +54,36 @@ type Porter struct {
54
54
Secrets secrets.Store
55
55
Signer signing.Signer
56
56
57
+ onClose []func () error
58
+
57
59
// Deprecated: Use the individual storage providers in the Porter struct instead
58
60
// This is only here for backwards compatibility where MongoDB was the only storage provider.
59
61
Storage storage.Provider
60
62
}
61
63
62
64
// Options for configuring a new Porter client passed to NewWith.
63
65
type Options struct {
64
- Config * config.Config // Optional. Defaults to a config.New.
65
- SecretStorage secrets.Store // Optional. Defaults to a secrets.NewPluginAdapter(secretsplugin.NewStore).
66
- Signer signing.Signer // Optional. Defaults to a signing.NewPluginAdapter(signingplugin.NewSigner).
66
+ Config * config.Config // Optional. Defaults to a config.New.
67
+ Secrets secrets.Store // Optional. Defaults to a secrets.NewPluginAdapter(secretsplugin.NewStore).
68
+ Signer signing.Signer // Optional. Defaults to a signing.NewPluginAdapter(signingplugin.NewSigner).
67
69
}
68
70
69
71
// NewWith creates a new Porter client with useful defaults that can be overridden with the provided options.
72
+ //
73
+ // Porter.Connect must be called before using the Porter client and Porter.Close must be called when done.
70
74
func NewWith (opt Options ) (* Porter , error ) {
71
75
if opt .Config == nil {
72
76
opt .Config = config .New ()
73
77
}
74
- if opt .SecretStorage == nil {
75
- opt .SecretStorage = secrets .NewPluginAdapter (secretsplugin .NewStore (opt .Config ))
78
+ if opt .Secrets == nil {
79
+ opt .Secrets = secrets .NewPluginAdapter (secretsplugin .NewStore (opt .Config ))
76
80
}
77
81
if opt .Signer == nil {
78
82
opt .Signer = signing .NewPluginAdapter (signingplugin .NewSigner (opt .Config ))
79
83
}
80
84
81
- if p , ok := sql .IsPostgresStorage (opt .Config ); ok {
82
- po , err := newWithSQL (opt .Config , p , opt .SecretStorage , opt .Signer )
83
- if err != nil {
84
- return nil , err
85
- }
86
- return po , nil
87
- }
88
-
89
- storage := storage .NewPluginAdapter (storageplugin .NewStore (opt .Config ))
90
- return NewFor (opt .Config , storage , opt .SecretStorage , opt .Signer ), nil
85
+ // storage initialization is deferred until Connect is called where we certainly have the config loaded
86
+ return newWith (opt .Config , nil , nil , nil , opt .Secrets , opt .Signer , nil , nil ), nil
91
87
}
92
88
93
89
// New porter client, initialized with useful defaults.
@@ -103,6 +99,9 @@ func New() *Porter {
103
99
return NewFor (c , storage , secretStorage , signer )
104
100
}
105
101
102
+ // NewFor creates a new Porter client with the provided configuration and storage backend.
103
+ //
104
+ // Deprecated: Use NewWith instead. NewFor does not support SQL storage backends.
106
105
func NewFor (
107
106
c * config.Config ,
108
107
store storage.Store ,
@@ -120,36 +119,6 @@ func NewFor(
120
119
return newWith (c , installationStorage , credStorage , paramStorage , secretStorage , signer , sanitizerService , storageManager )
121
120
}
122
121
123
- func newWithSQL (
124
- c * config.Config ,
125
- p config.StoragePlugin ,
126
- secretStorage secrets.Store ,
127
- signer signing.Signer ,
128
- ) (* Porter , error ) {
129
- pc , err := sql .UnmarshalPluginConfig (p .GetConfig ())
130
- if err != nil {
131
- return nil , fmt .Errorf ("could not unmarshal plugin config: %s" , err )
132
- }
133
- if pc .URL == "" {
134
- return nil , errors .New ("no URL provided in plugin config" )
135
- }
136
- _ , err = url .Parse (pc .URL )
137
- if err != nil {
138
- return nil , fmt .Errorf ("invalid URL provided in plugin config: %s" , err )
139
- }
140
- db , err := gorm .Open (postgres .Open (pc .URL ))
141
- if err != nil {
142
- return nil , fmt .Errorf ("could not open database: %s" , err )
143
- }
144
-
145
- installationStorage := storage .NewInstallationStoreSQL (db )
146
- credStorage := storage .NewCredentialStoreSQL (db , secretStorage )
147
- paramStorage := storage .NewParameterStoreSQL (db , secretStorage )
148
- sanitizerService := storage .NewSanitizer (paramStorage , secretStorage )
149
-
150
- return newWith (c , installationStorage , credStorage , paramStorage , secretStorage , signer , sanitizerService , nil ), nil
151
- }
152
-
153
122
func newWith (
154
123
c * config.Config ,
155
124
installationStorage storage.InstallationProvider ,
@@ -160,6 +129,10 @@ func newWith(
160
129
sanitizerService * storage.Sanitizer ,
161
130
storageManager storage.Provider ,
162
131
) * Porter {
132
+ var cnab cnabprovider.CNABProvider
133
+ if installationStorage != nil && credStorage != nil && paramStorage != nil && secretStorage != nil && sanitizerService != nil {
134
+ cnab = cnabprovider .NewRuntime (c , installationStorage , credStorage , paramStorage , secretStorage , sanitizerService )
135
+ }
163
136
164
137
return & Porter {
165
138
Config : c ,
@@ -173,7 +146,7 @@ func newWith(
173
146
Templates : templates .NewTemplates (c ),
174
147
Mixins : mixin .NewPackageManager (c ),
175
148
Plugins : plugins .NewPackageManager (c ),
176
- CNAB : cnabprovider . NewRuntime ( c , installationStorage , credStorage , paramStorage , secretStorage , sanitizerService ) ,
149
+ CNAB : cnab ,
177
150
Sanitizer : sanitizerService ,
178
151
Signer : signer ,
179
152
}
@@ -196,8 +169,66 @@ func (p *Porter) Connect(ctx context.Context) (context.Context, error) {
196
169
}
197
170
})
198
171
172
+ init := func (ctx context.Context ) error {
173
+ if p .Installations != nil && p .Credentials != nil && p .Parameters != nil && p .Sanitizer != nil {
174
+ return nil // already initialized
175
+ }
176
+
177
+ storagePlugin , ok := sql .IsPostgresStorage (p .Config )
178
+ if ! ok {
179
+ store := storage .NewPluginAdapter (storageplugin .NewStore (p .Config ))
180
+ mgr := migrations .NewManager (p .Config , store )
181
+
182
+ p .Storage = mgr
183
+ p .Installations = storage .NewInstallationStore (p .Storage )
184
+ p .Credentials = storage .NewCredentialStore (p .Storage , p .Secrets )
185
+ p .Parameters = storage .NewParameterStore (p .Storage , p .Secrets )
186
+ p .Sanitizer = storage .NewSanitizer (p .Parameters , p .Secrets )
187
+
188
+ mgr .Initialize (p .Sanitizer ) // we have a bit of a dependency problem here that it would be great to figure out eventually
189
+ return nil
190
+ }
191
+
192
+ // Initialize the SQL storage backend
193
+ pc , err := sql .UnmarshalPluginConfig (storagePlugin .GetConfig ())
194
+ if err != nil {
195
+ return fmt .Errorf ("could not unmarshal plugin config: %s" , err )
196
+ }
197
+ if pc .URL == "" {
198
+ return errors .New ("no URL provided in plugin config" )
199
+ }
200
+ db , err := gorm .Open (postgres .Open (pc .URL ))
201
+ if err != nil {
202
+ return fmt .Errorf ("could not open database: %s" , err )
203
+ }
204
+ closeFn := func () error {
205
+ if sqlDB , err := db .DB (); err == nil {
206
+ return sqlDB .Close ()
207
+ }
208
+ return nil
209
+ }
210
+ if err = migrate .MigrateDB (ctx , db ); err != nil {
211
+ _ = closeFn ()
212
+ return err
213
+ }
214
+
215
+ p .onClose = append (p .onClose , closeFn )
216
+
217
+ if p .Installations == nil {
218
+ p .Installations = storage .NewInstallationStoreSQL (db )
219
+ }
220
+ if p .Credentials == nil {
221
+ p .Credentials = storage .NewCredentialStoreSQL (db , p .Secrets )
222
+ }
223
+ if p .Parameters == nil {
224
+ p .Parameters = storage .NewParameterStoreSQL (db , p .Secrets )
225
+ }
226
+
227
+ return nil
228
+ }
229
+
199
230
// Load the config file and replace any referenced secrets
200
- return p .Config .Load (ctx , func (innerCtx context.Context , secret string ) (string , error ) {
231
+ ctx , err := p .Config .Load (ctx , func (innerCtx context.Context , secret string ) (string , error ) {
201
232
value , err := p .Secrets .Resolve (innerCtx , "secret" , secret )
202
233
if err != nil {
203
234
if strings .Contains (err .Error (), "invalid value source: secret" ) {
@@ -207,6 +238,22 @@ func (p *Porter) Connect(ctx context.Context) (context.Context, error) {
207
238
}
208
239
return value , nil
209
240
})
241
+ if err != nil {
242
+ return ctx , err
243
+ }
244
+
245
+ if err = init (ctx ); err != nil {
246
+ return ctx , err
247
+ }
248
+
249
+ if p .Sanitizer == nil {
250
+ p .Sanitizer = storage .NewSanitizer (p .Parameters , p .Secrets )
251
+ }
252
+ if p .CNAB == nil {
253
+ p .CNAB = cnabprovider .NewRuntime (p .Config , p .Installations , p .Credentials , p .Parameters , p .Secrets , p .Sanitizer )
254
+ }
255
+
256
+ return ctx , nil
210
257
}
211
258
212
259
// Close releases resources used by Porter before terminating the application.
@@ -225,6 +272,11 @@ func (p *Porter) Close() error {
225
272
bigErr = multierror .Append (bigErr , err )
226
273
}
227
274
}
275
+ for _ , do := range p .onClose {
276
+ if err = do (); err != nil {
277
+ bigErr = multierror .Append (bigErr , err )
278
+ }
279
+ }
228
280
229
281
err = p .Config .Close ()
230
282
if err != nil {
0 commit comments