Skip to content

Commit c7fe66e

Browse files
committed
cmd/schedule-builder: Add CLI tool for release schedule generation
To parse the schedule found in the sig-release repo
1 parent a977669 commit c7fe66e

File tree

7 files changed

+374
-0
lines changed

7 files changed

+374
-0
lines changed

BUILD.bazel

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ filegroup(
3333
"//cmd/krel:all-srcs",
3434
"//cmd/kubepkg:all-srcs",
3535
"//cmd/release-notes:all-srcs",
36+
"//cmd/schedule-builder:all-srcs",
3637
"//lib:all-srcs",
3738
"//pkg/announce:all-srcs",
3839
"//pkg/command:all-srcs",

cmd/schedule-builder/BUILD.bazel

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2+
3+
go_library(
4+
name = "go_default_library",
5+
srcs = ["main.go"],
6+
importpath = "k8s.io/release/cmd/schedule-builder",
7+
visibility = ["//visibility:private"],
8+
deps = ["//cmd/schedule-builder/cmd:go_default_library"],
9+
)
10+
11+
filegroup(
12+
name = "package-srcs",
13+
srcs = glob(["**"]),
14+
tags = ["automanaged"],
15+
visibility = ["//visibility:private"],
16+
)
17+
18+
filegroup(
19+
name = "all-srcs",
20+
srcs = [
21+
":package-srcs",
22+
"//cmd/schedule-builder/cmd:all-srcs",
23+
],
24+
tags = ["automanaged"],
25+
visibility = ["//visibility:public"],
26+
)
27+
28+
go_binary(
29+
name = "schedule-builder",
30+
embed = [":go_default_library"],
31+
visibility = ["//visibility:public"],
32+
)

cmd/schedule-builder/README.md

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# Schedule Builder
2+
3+
This simple tool has the objective to parse the yaml file located in [SIG-Release](https://github.com/kubernetes/sig-release/blob/master/releases/), which shows the scheduled and past patch releases of the current Kubernetes Release cycle in machine readable format.
4+
5+
## Install
6+
7+
The simplest way to install the `schedule-builder` CLI is via `go get`:
8+
9+
```
10+
$ go get k8s.io/release/cmd/schedule-builder
11+
```
12+
13+
This will install `schedule-builder` to `$(go env GOPATH)/bin/schedule-builder`.
14+
15+
Also if you have the `kubernetes/release` cloned you can run the `make release-tools` to build all the tools.
16+
17+
## Usage
18+
19+
To run this tool you can just do, assuming you have cloned both `SIG-Release` and `Release` repositories, like
20+
21+
```
22+
.
23+
+-- kubernetes
24+
| +-- sig-release
25+
| +-- release
26+
```
27+
28+
```bash
29+
$ schedule-builder --schedule-path ../sig-release/releases/schedule.yaml
30+
```
31+
32+
The output will be something similiar to this
33+
34+
```
35+
### Timeline
36+
37+
### 1.18
38+
39+
Next patch release is **1.18.4**
40+
41+
End of Life for **1.18** is **TBD**
42+
43+
| PATCH RELEASE | CHERRY PICK DEADLINE | TARGET DATE |
44+
|---------------|-----------------------|-------------|
45+
| 1.18.4 | 2020-06-12 | 2020-06-17 |
46+
| 1.18.3 | 2020-05-15 | 2020-05-20 |
47+
| 1.18.2 | 2020-04-13 | 2020-04-16 |
48+
| 1.18.1 | 2020-04-06 | 2020-04-08 |
49+
50+
### 1.17
51+
52+
Next patch release is **1.17.7**
53+
54+
End of Life for **1.17** is **TBD**
55+
56+
| PATCH RELEASE | CHERRY PICK DEADLINE | TARGET DATE |
57+
|---------------|---------------------------------------------------------------------------------------|-------------|
58+
| 1.17.7 | 2020-06-12 | 2020-06-17 |
59+
| 1.17.6 | 2020-05-15 | 2020-05-20 |
60+
| 1.17.5 | 2020-04-13 | 2020-04-16 |
61+
| 1.17.4 | 2020-03-09 | 2020-03-12 |
62+
| 1.17.3 | 2020-02-07 | 2020-02-11 |
63+
| 1.17.2 | No-op release https://groups.google.com/d/topic/kubernetes-dev/Mhpx-loSBns/discussion | 2020-01-21 |
64+
| 1.17.1 | 2020-01-10 | 2020-01-14 |
65+
66+
### 1.16
67+
68+
Next patch release is **1.16.11**
69+
70+
End of Life for **1.16** is **TBD**
71+
72+
| PATCH RELEASE | CHERRY PICK DEADLINE | TARGET DATE |
73+
|---------------|---------------------------------------------------------------------------------------|-------------|
74+
| 1.16.11 | 2020-06-12 | 2020-06-17 |
75+
| 1.16.10 | 2020-05-15 | 2020-05-20 |
76+
| 1.16.9 | 2020-04-13 | 2020-04-16 |
77+
| 1.16.8 | 2020-03-09 | 2020-03-12 |
78+
| 1.16.7 | 2020-02-07 | 2020-02-11 |
79+
| 1.16.6 | No-op release https://groups.google.com/d/topic/kubernetes-dev/Mhpx-loSBns/discussion | 2020-01-21 |
80+
| 1.16.5 | 2020-01-10 | 2020-01-14 |
81+
| 1.16.4 | 2019-12-06 | 2019-12-11 |
82+
| 1.16.3 | 2019-11-08 | 2019-11-13 |
83+
| 1.16.2 | 2019-10-11 | 2019-10-15 |
84+
| 1.16.1 | 2019-09-27 | 2019-10-02 |
85+
```
86+
87+
Also can save the schedule in a file, to do that, you can set the `--output-file` flag together with the filename.
88+
89+
```
90+
$ schedule-builder --schedule-path ../sig-release/releases/schedule.yaml --output-file my-schedule.md
91+
```

cmd/schedule-builder/cmd/BUILD.bazel

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
load("@io_bazel_rules_go//go:def.bzl", "go_library")
2+
3+
go_library(
4+
name = "go_default_library",
5+
srcs = ["root.go"],
6+
importpath = "k8s.io/release/cmd/schedule-builder/cmd",
7+
visibility = ["//visibility:public"],
8+
deps = [
9+
"//pkg/log:go_default_library",
10+
"@com_github_olekukonko_tablewriter//:go_default_library",
11+
"@com_github_pkg_errors//:go_default_library",
12+
"@com_github_sirupsen_logrus//:go_default_library",
13+
"@com_github_spf13_cobra//:go_default_library",
14+
"@io_k8s_sigs_yaml//:go_default_library",
15+
],
16+
)
17+
18+
filegroup(
19+
name = "package-srcs",
20+
srcs = glob(["**"]),
21+
tags = ["automanaged"],
22+
visibility = ["//visibility:private"],
23+
)
24+
25+
filegroup(
26+
name = "all-srcs",
27+
srcs = [":package-srcs"],
28+
tags = ["automanaged"],
29+
visibility = ["//visibility:public"],
30+
)

cmd/schedule-builder/cmd/root.go

+196
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cmd
18+
19+
import (
20+
"fmt"
21+
"io/ioutil"
22+
"strings"
23+
24+
"github.com/olekukonko/tablewriter"
25+
"github.com/pkg/errors"
26+
"github.com/sirupsen/logrus"
27+
"github.com/spf13/cobra"
28+
29+
"k8s.io/release/pkg/log"
30+
"sigs.k8s.io/yaml"
31+
)
32+
33+
// PatchSchedule main struct to hold the schedules
34+
type PatchSchedule struct {
35+
Schedules []Schedule `yaml:"schedules"`
36+
}
37+
38+
// PreviousPatches struct to define the old pacth schedules
39+
type PreviousPatches struct {
40+
Release string `yaml:"release"`
41+
CherryPickDeadline string `yaml:"cherryPickDeadline"`
42+
TargetDate string `yaml:"targetDate"`
43+
}
44+
45+
// Schedule struct to define the release schedule for a specific version
46+
type Schedule struct {
47+
Release string `yaml:"release"`
48+
Next string `yaml:"next"`
49+
CherryPickDeadline string `yaml:"cherryPickDeadline"`
50+
TargetDate string `yaml:"targetDate"`
51+
EndOfLifeDate string `yaml:"endOfLifeDate"`
52+
PreviousPatches []PreviousPatches `yaml:"previousPatches"`
53+
}
54+
55+
// rootCmd represents the base command when called without any subcommands
56+
var rootCmd = &cobra.Command{
57+
Use: "schedule-builder --config-path path/to/schedule.yaml [--output-file <filename.md>]",
58+
Short: "schedule-builder generate a humam readable format of the Kubernetes release schedule",
59+
Example: "schedule-builder --config-path /home/user/kubernetes/sig-release/releases/schedule.yaml",
60+
SilenceUsage: true,
61+
SilenceErrors: true,
62+
PersistentPreRunE: initLogging,
63+
RunE: func(*cobra.Command, []string) error {
64+
return run(opts)
65+
},
66+
}
67+
68+
type options struct {
69+
configPath string
70+
outputFile string
71+
logLevel string
72+
}
73+
74+
var opts = &options{}
75+
76+
const (
77+
configPathFlag = "config-path"
78+
outputFileFlag = "output-file"
79+
)
80+
81+
var requiredFlags = []string{
82+
configPathFlag,
83+
}
84+
85+
// Execute adds all child commands to the root command and sets flags appropriately.
86+
// This is called by main.main(). It only needs to happen once to the rootCmd.
87+
func Execute() {
88+
if err := rootCmd.Execute(); err != nil {
89+
logrus.Fatal(err)
90+
}
91+
}
92+
93+
func init() {
94+
rootCmd.PersistentFlags().StringVar(
95+
&opts.configPath,
96+
configPathFlag,
97+
"",
98+
"path where can find the schedule.yaml file",
99+
)
100+
101+
rootCmd.PersistentFlags().StringVar(
102+
&opts.outputFile,
103+
outputFileFlag,
104+
"",
105+
"name of the file that save the schedule to. If not set it will just output to the stdout.",
106+
)
107+
108+
rootCmd.PersistentFlags().StringVar(
109+
&opts.logLevel,
110+
"log-level",
111+
"info",
112+
"the logging verbosity, either 'panic', 'fatal', 'error', 'warn', 'warning', 'info', 'debug' or 'trace'",
113+
)
114+
115+
for _, flag := range requiredFlags {
116+
if err := rootCmd.MarkPersistentFlagRequired(flag); err != nil {
117+
logrus.Fatal(err)
118+
}
119+
}
120+
}
121+
122+
func initLogging(*cobra.Command, []string) error {
123+
return log.SetupGlobalLogger(opts.logLevel)
124+
}
125+
126+
func run(opts *options) error {
127+
if err := opts.SetAndValidate(); err != nil {
128+
return errors.Wrap(err, "validating schedule-path options")
129+
}
130+
131+
logrus.Infof("Reading the schedule file %s...", opts.configPath)
132+
data, err := ioutil.ReadFile(opts.configPath)
133+
if err != nil {
134+
return errors.Wrap(err, "failed to read the file")
135+
}
136+
137+
var patchSchedule PatchSchedule
138+
139+
logrus.Info("Parsing the schedule...")
140+
err = yaml.UnmarshalStrict(data, &patchSchedule)
141+
if err != nil {
142+
return errors.Wrap(err, "failed to decode the file")
143+
}
144+
145+
logrus.Info("Generating the markdown output...")
146+
147+
output := []string{}
148+
output = append(output, "### Timeline\n")
149+
for _, releaseSchedule := range patchSchedule.Schedules {
150+
output = append(output, fmt.Sprintf("### %s\n", releaseSchedule.Release),
151+
fmt.Sprintf("Next patch release is **%s**\n", releaseSchedule.Next),
152+
fmt.Sprintf("End of Life for **%s** is **%s**\n", releaseSchedule.Release, releaseSchedule.EndOfLifeDate))
153+
154+
tableString := &strings.Builder{}
155+
table := tablewriter.NewWriter(tableString)
156+
table.SetAutoWrapText(false)
157+
table.SetHeader([]string{"Patch Release", "Cherry Pick Deadline", "Target Date"})
158+
table.Append([]string{strings.TrimSpace(releaseSchedule.Next), strings.TrimSpace(releaseSchedule.CherryPickDeadline), strings.TrimSpace(releaseSchedule.TargetDate)})
159+
160+
for _, previous := range releaseSchedule.PreviousPatches {
161+
table.Append([]string{strings.TrimSpace(previous.Release), strings.TrimSpace(previous.CherryPickDeadline), strings.TrimSpace(previous.TargetDate)})
162+
}
163+
table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
164+
table.SetCenterSeparator("|")
165+
table.Render()
166+
167+
output = append(output, tableString.String())
168+
}
169+
170+
scheduleOut := strings.Join(output, "\n")
171+
172+
logrus.Info("Schedule parsed")
173+
println(scheduleOut)
174+
175+
if opts.outputFile != "" {
176+
logrus.Infof("Saving schedule to a file %s.", opts.outputFile)
177+
err := ioutil.WriteFile(opts.outputFile, []byte(scheduleOut), 0644)
178+
if err != nil {
179+
return errors.Wrap(err, "failed to save schedule to the file")
180+
}
181+
logrus.Info("File saved")
182+
}
183+
184+
return nil
185+
}
186+
187+
// SetAndValidate sets some default options and verifies if options are valid
188+
func (o *options) SetAndValidate() error {
189+
logrus.Info("Validating schedule-path options...")
190+
191+
if o.configPath == "" {
192+
return errors.Errorf("need to set the config-path")
193+
}
194+
195+
return nil
196+
}

cmd/schedule-builder/main.go

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package main
18+
19+
import "k8s.io/release/cmd/schedule-builder/cmd"
20+
21+
func main() {
22+
cmd.Execute()
23+
}

compile-release-tools

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ RELEASE_TOOLS=(
2323
gh2gcs
2424
kubepkg
2525
krel
26+
schedule-builder
2627
)
2728

2829
setup_env() {

0 commit comments

Comments
 (0)