forked from openshift/origin
-
Notifications
You must be signed in to change notification settings - Fork 1
/
builds.go
199 lines (166 loc) · 6.21 KB
/
builds.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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
package prune
import (
"fmt"
"io"
"os"
"text/tabwriter"
"time"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
buildapi "github.com/openshift/origin/pkg/build/apis/build"
buildclient "github.com/openshift/origin/pkg/build/client"
buildinternalclient "github.com/openshift/origin/pkg/build/generated/internalclientset"
"github.com/openshift/origin/pkg/oc/cli/builds/prune"
"github.com/openshift/origin/pkg/oc/cli/util/clientcmd"
)
const PruneBuildsRecommendedName = "builds"
var (
buildsLongDesc = templates.LongDesc(`
Prune old completed and failed builds
By default, the prune operation performs a dry run making no changes to internal registry. A
--confirm flag is needed for changes to be effective.`)
buildsExample = templates.Examples(`
# Dry run deleting older completed and failed builds and also including
# all builds whose associated BuildConfig no longer exists
%[1]s %[2]s --orphans
# To actually perform the prune operation, the confirm flag must be appended
%[1]s %[2]s --orphans --confirm`)
)
// PruneBuildsOptions holds all the required options for pruning builds.
type PruneBuildsOptions struct {
Confirm bool
Orphans bool
KeepYoungerThan time.Duration
KeepComplete int
KeepFailed int
Namespace string
BuildClient buildinternalclient.Interface
Out io.Writer
}
// NewCmdPruneBuilds implements the OpenShift cli prune builds command.
func NewCmdPruneBuilds(f *clientcmd.Factory, parentName, name string, out io.Writer) *cobra.Command {
opts := &PruneBuildsOptions{
Confirm: false,
Orphans: false,
KeepYoungerThan: 60 * time.Minute,
KeepComplete: 5,
KeepFailed: 1,
}
cmd := &cobra.Command{
Use: name,
Short: "Remove old completed and failed builds",
Long: buildsLongDesc,
Example: fmt.Sprintf(buildsExample, parentName, name),
Run: func(cmd *cobra.Command, args []string) {
kcmdutil.CheckErr(opts.Complete(f, cmd, args, out))
kcmdutil.CheckErr(opts.Validate())
kcmdutil.CheckErr(opts.Run())
},
}
cmd.Flags().BoolVar(&opts.Confirm, "confirm", opts.Confirm, "If true, specify that build pruning should proceed. Defaults to false, displaying what would be deleted but not actually deleting anything.")
cmd.Flags().BoolVar(&opts.Orphans, "orphans", opts.Orphans, "If true, prune all builds whose associated BuildConfig no longer exists and whose status is complete, failed, error, or cancelled.")
cmd.Flags().DurationVar(&opts.KeepYoungerThan, "keep-younger-than", opts.KeepYoungerThan, "Specify the minimum age of a Build for it to be considered a candidate for pruning.")
cmd.Flags().IntVar(&opts.KeepComplete, "keep-complete", opts.KeepComplete, "Per BuildConfig, specify the number of builds whose status is complete that will be preserved.")
cmd.Flags().IntVar(&opts.KeepFailed, "keep-failed", opts.KeepFailed, "Per BuildConfig, specify the number of builds whose status is failed, error, or cancelled that will be preserved.")
return cmd
}
// Complete turns a partially defined PruneBuildsOptions into a solvent structure
// which can be validated and used for pruning builds.
func (o *PruneBuildsOptions) Complete(f *clientcmd.Factory, cmd *cobra.Command, args []string, out io.Writer) error {
if len(args) > 0 {
return kcmdutil.UsageErrorf(cmd, "no arguments are allowed to this command")
}
o.Namespace = metav1.NamespaceAll
if cmd.Flags().Lookup("namespace").Changed {
var err error
o.Namespace, _, err = f.DefaultNamespace()
if err != nil {
return err
}
}
o.Out = out
config, err := f.ClientConfig()
if err != nil {
return err
}
buildClient, err := buildinternalclient.NewForConfig(config)
if err != nil {
return err
}
o.BuildClient = buildClient
return nil
}
// Validate ensures that a PruneBuildsOptions is valid and can be used to execute pruning.
func (o PruneBuildsOptions) Validate() error {
if o.KeepYoungerThan < 0 {
return fmt.Errorf("--keep-younger-than must be greater than or equal to 0")
}
if o.KeepComplete < 0 {
return fmt.Errorf("--keep-complete must be greater than or equal to 0")
}
if o.KeepFailed < 0 {
return fmt.Errorf("--keep-failed must be greater than or equal to 0")
}
return nil
}
// Run contains all the necessary functionality for the OpenShift cli prune builds command.
func (o PruneBuildsOptions) Run() error {
buildConfigList, err := o.BuildClient.Build().BuildConfigs(o.Namespace).List(metav1.ListOptions{})
if err != nil {
return err
}
buildConfigs := []*buildapi.BuildConfig{}
for i := range buildConfigList.Items {
buildConfigs = append(buildConfigs, &buildConfigList.Items[i])
}
buildList, err := o.BuildClient.Build().Builds(o.Namespace).List(metav1.ListOptions{})
if err != nil {
return err
}
builds := []*buildapi.Build{}
for i := range buildList.Items {
builds = append(builds, &buildList.Items[i])
}
options := prune.PrunerOptions{
KeepYoungerThan: o.KeepYoungerThan,
Orphans: o.Orphans,
KeepComplete: o.KeepComplete,
KeepFailed: o.KeepFailed,
BuildConfigs: buildConfigs,
Builds: builds,
}
pruner := prune.NewPruner(options)
w := tabwriter.NewWriter(o.Out, 10, 4, 3, ' ', 0)
defer w.Flush()
buildDeleter := &describingBuildDeleter{w: w}
if o.Confirm {
buildDeleter.delegate = buildclient.NewClientBuildClient(o.BuildClient)
} else {
fmt.Fprintln(os.Stderr, "Dry run enabled - no modifications will be made. Add --confirm to remove builds")
}
return pruner.Prune(buildDeleter)
}
// describingBuildDeleter prints information about each build it removes.
// If a delegate exists, its DeleteBuild function is invoked prior to returning.
type describingBuildDeleter struct {
w io.Writer
delegate buildclient.BuildDeleter
headerPrinted bool
}
var _ buildclient.BuildDeleter = &describingBuildDeleter{}
func (p *describingBuildDeleter) DeleteBuild(build *buildapi.Build) error {
if !p.headerPrinted {
p.headerPrinted = true
fmt.Fprintln(p.w, "NAMESPACE\tNAME")
}
fmt.Fprintf(p.w, "%s\t%s\n", build.Namespace, build.Name)
if p.delegate == nil {
return nil
}
if err := p.delegate.DeleteBuild(build); err != nil {
return err
}
return nil
}