@@ -6,8 +6,11 @@ package cmdliveinit
6
6
import (
7
7
"context"
8
8
"crypto/sha1"
9
+ goerrors "errors"
9
10
"fmt"
11
+ "io/ioutil"
10
12
"os"
13
+ "path/filepath"
11
14
"strconv"
12
15
"strings"
13
16
"time"
@@ -16,14 +19,17 @@ import (
16
19
"github.com/GoogleContainerTools/kpt/internal/errors"
17
20
"github.com/GoogleContainerTools/kpt/internal/pkg"
18
21
"github.com/GoogleContainerTools/kpt/internal/printer"
22
+ "github.com/GoogleContainerTools/kpt/internal/types"
19
23
"github.com/GoogleContainerTools/kpt/internal/util/attribution"
20
24
kptfilev1 "github.com/GoogleContainerTools/kpt/pkg/api/kptfile/v1"
25
+ rgfilev1alpha1 "github.com/GoogleContainerTools/kpt/pkg/api/resourcegroup/v1alpha1"
21
26
"github.com/GoogleContainerTools/kpt/pkg/kptfile/kptfileutil"
22
27
"github.com/spf13/cobra"
23
28
"k8s.io/cli-runtime/pkg/genericclioptions"
24
29
cmdutil "k8s.io/kubectl/pkg/cmd/util"
25
30
"sigs.k8s.io/cli-utils/pkg/common"
26
31
"sigs.k8s.io/cli-utils/pkg/config"
32
+ "sigs.k8s.io/kustomize/kyaml/yaml"
27
33
)
28
34
29
35
const defaultInventoryName = "inventory"
@@ -36,6 +42,24 @@ func (i *InvExistsError) Error() string {
36
42
return "inventory information already set for package"
37
43
}
38
44
45
+ // InvInRGExistsError defines new error when the inventory
46
+ // values have already been set on the ResourceGroup file and we will warn
47
+ // the user to migrate rather than init. This is part of kpt live STDIN work.
48
+ type InvInRGExistsError struct {}
49
+
50
+ func (i * InvInRGExistsError ) Error () string {
51
+ return "inventory information already set for package"
52
+ }
53
+
54
+ // InvInKfExistsError defines new error when the inventory
55
+ // values have already been set on the Kptfile and we will warn
56
+ // the user to migrate rather than init. This is part of kpt live STDIN work.
57
+ type InvInKfExistsError struct {}
58
+
59
+ func (i * InvInKfExistsError ) Error () string {
60
+ return "inventory information already set within Kptfile for package"
61
+ }
62
+
39
63
func NewRunner (ctx context.Context , factory cmdutil.Factory ,
40
64
ioStreams genericclioptions.IOStreams ) * Runner {
41
65
r := & Runner {
@@ -74,6 +98,7 @@ type Runner struct {
74
98
Force bool // Set inventory values even if already set in Kptfile
75
99
Name string // Inventory object name
76
100
namespace string // Inventory object namespace
101
+ RGFile string // resourcegroup object filepath
77
102
InventoryID string // Inventory object unique identifier label
78
103
Quiet bool // Output message during initialization
79
104
}
@@ -105,6 +130,7 @@ func (r *Runner) runE(_ *cobra.Command, args []string) error {
105
130
Quiet : r .Quiet ,
106
131
Name : r .Name ,
107
132
InventoryID : r .InventoryID ,
133
+ RGFileName : r .RGFile ,
108
134
Force : r .Force ,
109
135
}).Run (r .ctx )
110
136
if err != nil {
@@ -122,12 +148,22 @@ type ConfigureInventoryInfo struct {
122
148
123
149
Name string
124
150
InventoryID string
151
+ RGFileName string
125
152
126
153
Force bool
127
154
}
128
155
129
156
// Run updates the inventory info in the package given by the Path.
130
157
func (c * ConfigureInventoryInfo ) Run (ctx context.Context ) error {
158
+ // Use ResourceGroup file for inventory logic if the resourcegroup file
159
+ // is set directly. For this feature gate, the resourcegroup must be directly set
160
+ // through our tests since we are not exposing this through the command surface as a
161
+ // flag, currently. When we promote this, the resourcegroup filename can be empty and
162
+ // the default filename value will be inferred/used.
163
+ if c .RGFileName != "" {
164
+ return c .runLiveInitWithRGFile (ctx )
165
+ }
166
+
131
167
const op errors.Op = "cmdliveinit.Run"
132
168
pr := printer .FromContextOrDie (ctx )
133
169
@@ -182,6 +218,119 @@ func (c *ConfigureInventoryInfo) Run(ctx context.Context) error {
182
218
return nil
183
219
}
184
220
221
+ // func runLiveInitWithRGFile is a modified version of ConfigureInventoryInfo.Run that stores the
222
+ // package inventory information in a separate resourcegroup file. The logic for this is branched into
223
+ // a separate function to enable feature gating.
224
+ func (c * ConfigureInventoryInfo ) runLiveInitWithRGFile (ctx context.Context ) error {
225
+ const op errors.Op = "cmdliveinit.runLiveInitWithRGFile"
226
+ pr := printer .FromContextOrDie (ctx )
227
+
228
+ namespace , err := config .FindNamespace (c .Factory .ToRawKubeConfigLoader (), c .Pkg .UniquePath .String ())
229
+ if err != nil {
230
+ return errors .E (op , c .Pkg .UniquePath , err )
231
+ }
232
+ namespace = strings .TrimSpace (namespace )
233
+ if ! c .Quiet {
234
+ pr .Printf ("initializing ResourceGroup inventory info (namespace: %s)..." , namespace )
235
+ }
236
+
237
+ // Autogenerate the name if it is not provided through the flag.
238
+ if c .Name == "" {
239
+ randomSuffix := common .RandomStr ()
240
+ c .Name = fmt .Sprintf ("%s-%s" , defaultInventoryName , randomSuffix )
241
+ }
242
+
243
+ // Finally, create a ResourceGroup containing the inventory information.
244
+ err = createRGFile (c .Pkg , & kptfilev1.Inventory {
245
+ Namespace : namespace ,
246
+ Name : c .Name ,
247
+ InventoryID : c .InventoryID ,
248
+ }, c .RGFileName , c .Force )
249
+ if ! c .Quiet {
250
+ if err == nil {
251
+ pr .Printf ("success\n " )
252
+ } else {
253
+ pr .Printf ("failed\n " )
254
+ }
255
+ }
256
+ if err != nil {
257
+ return errors .E (op , c .Pkg .UniquePath , err )
258
+ }
259
+ // add metrics annotation to package resources to track the usage as the resources
260
+ // will be applied using kpt live group
261
+ at := attribution.Attributor {PackagePaths : []string {c .Pkg .UniquePath .String ()}, CmdGroup : "live" }
262
+ at .Process ()
263
+ return nil
264
+ }
265
+
266
+ // createRGFile fills in the inventory object values into the resourcegroup object and writes to file storage.
267
+ func createRGFile (p * pkg.Pkg , inv * kptfilev1.Inventory , filename string , force bool ) error {
268
+ const op errors.Op = "cmdliveinit.createRGFile"
269
+ // Read the resourcegroup object io io.dir
270
+ rg , err := p .ReadRGFile (filename )
271
+ if err != nil && ! goerrors .Is (err , os .ErrNotExist ) {
272
+ return errors .E (op , p .UniquePath , err )
273
+ }
274
+
275
+ // Read the Kptfile to ensure that inventory information is not in Kptfile either.
276
+ kf , err := p .Kptfile ()
277
+ if err != nil {
278
+ return errors .E (op , p .UniquePath , err )
279
+ }
280
+ // Validate the inventory values don't exist in Kptfile.
281
+ isEmpty := kptfileInventoryEmpty (kf .Inventory )
282
+ if ! isEmpty && ! force {
283
+ return errors .E (op , p .UniquePath , & InvInKfExistsError {})
284
+ }
285
+ // Set the Kptfile inventory to be nil if we force write to resourcegroup instead.
286
+ kf .Inventory = nil
287
+
288
+ // Validate the inventory values don't already exist in Resourcegroup.
289
+ if rg != nil && ! force {
290
+ return errors .E (op , p .UniquePath , & InvExistsError {})
291
+ }
292
+ // Initialize new resourcegroup object, as rg should have been nil.
293
+ rg = & rgfilev1alpha1.ResourceGroup {ResourceMeta : rgfilev1alpha1 .DefaultMeta }
294
+ // // Finally, set the inventory parameters in the ResourceGroup object and write it.
295
+ rg .Name = inv .Name
296
+ rg .Namespace = inv .Namespace
297
+ if inv .InventoryID != "" {
298
+ rg .Labels = map [string ]string {rgfilev1alpha1 .RGInventoryIDLabel : inv .InventoryID }
299
+ }
300
+ if err := writeRGFile (p .UniquePath .String (), rg , filename ); err != nil {
301
+ return errors .E (op , p .UniquePath , err )
302
+ }
303
+
304
+ // Rewrite Kptfile without inventory existing Kptfile contains inventory info. This
305
+ // is required when a user appends the force flag.
306
+ if ! isEmpty {
307
+ if err := kptfileutil .WriteFile (p .UniquePath .String (), kf ); err != nil {
308
+ return errors .E (op , p .UniquePath , err )
309
+ }
310
+ }
311
+
312
+ return nil
313
+ }
314
+
315
+ // writeRGFile writes a ResourceGroup inventory to local disk.
316
+ func writeRGFile (dir string , rg * rgfilev1alpha1.ResourceGroup , filename string ) error {
317
+ const op errors.Op = "cmdliveinit.writeRGFile"
318
+ b , err := yaml .MarshalWithOptions (rg , & yaml.EncoderOptions {SeqIndent : yaml .WideSequenceStyle })
319
+ if err != nil {
320
+ return err
321
+ }
322
+ if _ , err := os .Stat (filepath .Join (dir , filename )); err != nil && ! goerrors .Is (err , os .ErrNotExist ) {
323
+ return errors .E (op , errors .IO , types .UniquePath (dir ), err )
324
+ }
325
+
326
+ // fyi: perm is ignored if the file already exists
327
+ err = ioutil .WriteFile (filepath .Join (dir , filename ), b , 0600 )
328
+ if err != nil {
329
+ return errors .E (op , errors .IO , types .UniquePath (dir ), err )
330
+ }
331
+ return nil
332
+ }
333
+
185
334
// Run fills in the inventory object values into the Kptfile.
186
335
func updateKptfile (p * pkg.Pkg , inv * kptfilev1.Inventory , force bool ) error {
187
336
const op errors.Op = "cmdliveinit.updateKptfile"
0 commit comments