forked from ko-build/ko
-
Notifications
You must be signed in to change notification settings - Fork 0
/
filestuff.go
133 lines (123 loc) · 4.07 KB
/
filestuff.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
// Copyright 2018 Google LLC All Rights Reserved.
//
// 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 options
import (
"log"
"os"
"path/filepath"
"github.com/fsnotify/fsnotify"
"github.com/spf13/cobra"
)
// FilenameOptions is from pkg/kubectl.
type FilenameOptions struct {
Filenames []string
Recursive bool
Watch bool
}
func AddFileArg(cmd *cobra.Command, fo *FilenameOptions) {
// From pkg/kubectl
cmd.Flags().StringSliceVarP(&fo.Filenames, "filename", "f", fo.Filenames,
"Filename, directory, or URL to files to use to create the resource")
cmd.Flags().BoolVarP(&fo.Recursive, "recursive", "R", fo.Recursive,
"Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory.")
cmd.Flags().BoolVarP(&fo.Watch, "watch", "W", fo.Watch,
"Continuously monitor the transitive dependencies of the passed yaml files, and redeploy whenever anything changes.")
}
// Based heavily on pkg/kubectl
func EnumerateFiles(fo *FilenameOptions) chan string {
files := make(chan string)
go func() {
// When we're done enumerating files, close the channel
defer close(files)
// When we are in --watch mode, we set up watches on the filesystem locations
// that we are supplied and continuously stream files, until we are sent an
// interrupt.
var watcher *fsnotify.Watcher
if fo.Watch {
var err error
watcher, err = fsnotify.NewWatcher()
if err != nil {
log.Fatalf("Unexpected error initializing fsnotify: %v", err)
}
defer watcher.Close()
}
for _, paths := range fo.Filenames {
// Just pass through '-' as it is indicative of stdin.
if paths == "-" {
files <- paths
continue
}
// For each of the "filenames" we are passed (file or directory) start a
// "Walk" to enumerate all of the contained files recursively.
err := filepath.Walk(paths, func(path string, fi os.FileInfo, err error) error {
if err != nil {
return err
}
// If this is a directory, skip it if it isn't the current directory we are
// processing (unless we are in recursive mode). If we decide to process
// the directory, and we're in watch mode, then we set up a watch on the
// directory.
if fi.IsDir() {
if path != paths && !fo.Recursive {
return filepath.SkipDir
}
if watcher != nil {
watcher.Add(path)
}
// We don't stream back directories, we just decide to skip them, or not.
return nil
}
// Don't check extension if the filepath was passed explicitly
if path != paths {
switch filepath.Ext(path) {
case ".json", ".yaml":
// Process these.
default:
return nil
}
// We weren't passed this explicitly, so elide the watch as we
// are already watching the directory.
} else {
// We were passed this directly, and so we may not be watching the
// directory, so watch this file explicitly.
if watcher != nil {
watcher.Add(path)
}
}
files <- path
return nil
})
if err != nil {
log.Fatalf("Error enumerating files: %v", err)
}
}
// We're done watching the files we were passed and setting up watches.
// Now listen for change events from the watches we set up and resend
// files that change as if we just saw them (so they can be reprocessed).
if watcher != nil {
for {
select {
case event := <-watcher.Events:
switch filepath.Ext(event.Name) {
case ".json", ".yaml":
files <- event.Name
}
case err := <-watcher.Errors:
log.Fatalf("Error watching: %v", err)
}
}
}
}()
return files
}