@@ -3,9 +3,11 @@ package apply
3
3
import (
4
4
"context"
5
5
"fmt"
6
+ "golang.org/x/exp/slices"
6
7
"io"
7
8
"net/http"
8
9
"os"
10
+ "path/filepath"
9
11
"strings"
10
12
"time"
11
13
@@ -28,6 +30,8 @@ const (
28
30
timeout = 10 * time .Second
29
31
)
30
32
33
+ var yamlExt = []string {".yaml" , ".yml" }
34
+
31
35
type applyContext struct {
32
36
* kumactl_cmd.RootContext
33
37
@@ -63,66 +67,55 @@ $ kumactl apply -f https://example.com/resource.yaml
63
67
64
68
var b []byte
65
69
var err error
70
+ var resources []model.Resource
66
71
67
72
if ctx .args .file == "-" {
68
73
b , err = io .ReadAll (cmd .InOrStdin ())
69
74
if err != nil {
70
75
return err
71
76
}
72
- } else {
73
- if strings .HasPrefix (ctx .args .file , "http://" ) || strings .HasPrefix (ctx .args .file , "https://" ) {
74
- client := & http.Client {
75
- Timeout : timeout ,
76
- }
77
- req , err := http .NewRequest ("GET" , ctx .args .file , nil )
78
- if err != nil {
79
- return errors .Wrap (err , "error creating new http request" )
80
- }
81
- resp , err := client .Do (req )
82
- if err != nil {
83
- return errors .Wrap (err , "error with GET http request" )
84
- }
85
- if resp .StatusCode != http .StatusOK {
86
- return errors .Wrap (err , "error while retrieving URL" )
87
- }
88
- defer resp .Body .Close ()
89
- b , err = io .ReadAll (resp .Body )
90
- if err != nil {
91
- return errors .Wrap (err , "error while reading provided file" )
92
- }
93
- } else {
94
- b , err = os .ReadFile (ctx .args .file )
95
- if err != nil {
96
- return errors .Wrap (err , "error while reading provided file" )
97
- }
77
+ if len (b ) == 0 {
78
+ return fmt .Errorf ("no resource(s) passed to apply" )
98
79
}
99
- }
100
- if len (b ) == 0 {
101
- return fmt .Errorf ("no resource(s) passed to apply" )
102
- }
103
- var resources []model.Resource
104
- rawResources := yaml .SplitYAML (string (b ))
105
- for _ , rawResource := range rawResources {
106
- if len (rawResource ) == 0 {
107
- continue
80
+ r , err := bytesToResources (ctx , cmd , b )
81
+ if err != nil {
82
+ return errors .Wrap (err , "error parsing file to resources" )
108
83
}
109
- bytes := []byte (rawResource )
110
- if len (ctx .args .vars ) > 0 {
111
- bytes = template .Render (rawResource , ctx .args .vars )
84
+ resources = append (resources , r ... )
85
+ } else if strings .HasPrefix (ctx .args .file , "http://" ) || strings .HasPrefix (ctx .args .file , "https://" ) {
86
+ client := & http.Client {
87
+ Timeout : timeout ,
112
88
}
113
- res , err := rest_types . YAML . UnmarshalCore ( bytes )
89
+ req , err := http . NewRequest ( "GET" , ctx . args . file , nil )
114
90
if err != nil {
115
- return errors .Wrap (err , "YAML contains invalid resource " )
91
+ return errors .Wrap (err , "error creating new http request " )
116
92
}
117
- if err , msg := mesh .ValidateMetaBackwardsCompatible (res .GetMeta (), res .Descriptor ().Scope ); err .HasViolations () {
118
- return err .OrNil ()
119
- } else if msg != "" {
120
- if _ , printErr := fmt .Fprintln (cmd .ErrOrStderr (), msg ); printErr != nil {
121
- return printErr
122
- }
93
+ resp , err := client .Do (req )
94
+ if err != nil {
95
+ return errors .Wrap (err , "error with GET http request" )
96
+ }
97
+ if resp .StatusCode != http .StatusOK {
98
+ return errors .Wrap (err , "error while retrieving URL" )
99
+ }
100
+ defer resp .Body .Close ()
101
+ b , err = io .ReadAll (resp .Body )
102
+ if err != nil {
103
+ return errors .Wrap (err , "error while reading provided file" )
104
+ }
105
+ r , err := bytesToResources (ctx , cmd , b )
106
+ if err != nil {
107
+ return errors .Wrap (err , "error parsing file to resources" )
123
108
}
124
- resources = append (resources , res )
109
+ resources = append (resources , r ... )
110
+ } else {
111
+ // Process local yaml files
112
+ r , err := localFileToResources (ctx , cmd )
113
+ if err != nil {
114
+ return errors .Wrap (err , "error processing file" )
115
+ }
116
+ resources = append (resources , r ... )
125
117
}
118
+
126
119
var rs store.ResourceStore
127
120
if ! ctx .args .dryRun {
128
121
rs , err = pctx .CurrentResourceStore ()
@@ -158,7 +151,96 @@ $ kumactl apply -f https://example.com/resource.yaml
158
151
return cmd
159
152
}
160
153
161
- func upsert (ctx context.Context , typeRegistry registry.TypeRegistry , rs store.ResourceStore , res model.Resource ) ([]string , error ) {
154
+ // localFileToResources reads and converts a local file into a list of model.Resource
155
+ // the local file could be a directory, in which case it processes all the yaml files in the directory
156
+ func localFileToResources (ctx * applyContext , cmd * cobra.Command ) ([]model.Resource , error ) {
157
+ var resources []model.Resource
158
+ file , err := os .Open (ctx .args .file )
159
+ if err != nil {
160
+ return nil , errors .Wrap (err , "error while opening provided file" )
161
+ }
162
+ defer file .Close ()
163
+ orgDir , _ := filepath .Split (ctx .args .file )
164
+
165
+ fileInfo , err := file .Stat ()
166
+ if err != nil {
167
+ return nil , errors .Wrap (err , "error getting stats for the provided file" )
168
+ }
169
+
170
+ var yamlFiles []string
171
+ if fileInfo .IsDir () {
172
+ for {
173
+ names , err := file .Readdirnames (10 )
174
+ if err != nil {
175
+ if err == io .EOF {
176
+ break
177
+ } else {
178
+ return nil , errors .Wrap (err , "error reading file names in directory" )
179
+ }
180
+ }
181
+ for _ , n := range names {
182
+ if slices .Contains (yamlExt , filepath .Ext (n )) {
183
+ yamlFiles = append (yamlFiles , n )
184
+ }
185
+ }
186
+ }
187
+ } else {
188
+ if slices .Contains (yamlExt , filepath .Ext (fileInfo .Name ())) {
189
+ yamlFiles = append (yamlFiles , fileInfo .Name ())
190
+ }
191
+ // TODO should this check be added?
192
+ //else {
193
+ // return nil, fmt.Errorf("error the specified input file extension isn't yaml")
194
+ //}
195
+ }
196
+ var b []byte
197
+ for _ , f := range yamlFiles {
198
+ joined := filepath .Join (orgDir , f )
199
+ b , err = os .ReadFile (joined )
200
+ if err != nil {
201
+ return nil , errors .Wrap (err , fmt .Sprintf ("error while reading the provided file [%s]" , f ))
202
+ }
203
+ r , err := bytesToResources (ctx , cmd , b )
204
+ if err != nil {
205
+ return nil , errors .Wrap (err , fmt .Sprintf ("error parsing file [%s] to resources" , f ))
206
+ }
207
+ resources = append (resources , r ... )
208
+ }
209
+ if len (resources ) == 0 {
210
+ return nil , fmt .Errorf ("no resource(s) passed to apply" )
211
+ }
212
+ return resources , nil
213
+ }
214
+
215
+ // bytesToResources converts a slice of bytes into a slice of model.Resource
216
+ func bytesToResources (ctx * applyContext , cmd * cobra.Command , fileBytes []byte ) ([]model.Resource , error ) {
217
+ var resources []model.Resource
218
+ rawResources := yaml .SplitYAML (string (fileBytes ))
219
+ for _ , rawResource := range rawResources {
220
+ if len (rawResource ) == 0 {
221
+ continue
222
+ }
223
+ bytes := []byte (rawResource )
224
+ if len (ctx .args .vars ) > 0 {
225
+ bytes = template .Render (rawResource , ctx .args .vars )
226
+ }
227
+ res , err := rest_types .YAML .UnmarshalCore (bytes )
228
+ if err != nil {
229
+ return nil , errors .Wrap (err , "YAML contains invalid resource" )
230
+ }
231
+ if err , msg := mesh .ValidateMetaBackwardsCompatible (res .GetMeta (), res .Descriptor ().Scope ); err .HasViolations () {
232
+ return nil , err .OrNil ()
233
+ } else if msg != "" {
234
+ if _ , printErr := fmt .Fprintln (cmd .ErrOrStderr (), msg ); printErr != nil {
235
+ return nil , printErr
236
+ }
237
+ }
238
+ resources = append (resources , res )
239
+ }
240
+ return resources , nil
241
+ }
242
+
243
+ func upsert (ctx context.Context , typeRegistry registry.TypeRegistry , rs store.ResourceStore , res model.Resource ) error {
162
244
newRes , err := typeRegistry .NewObject (res .Descriptor ().Name )
163
245
if err != nil {
164
246
return nil , err
0 commit comments