Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Command to check Rancher dependencies for a RC #286

Merged
merged 29 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
4dfe570
change repo files
johnatasr Oct 14, 2023
e0620ec
func to check all deps and command
johnatasr Oct 17, 2023
54fbdda
add test
johnatasr Oct 17, 2023
ea323c5
last adjusts
johnatasr Oct 19, 2023
0c968b8
improve code
johnatasr Oct 19, 2023
ff54210
merge
johnatasr Oct 19, 2023
df575f1
change requires
johnatasr Oct 19, 2023
98cc14d
remove necessity of repo and org
johnatasr Oct 23, 2023
b2d1773
more improves
johnatasr Oct 26, 2023
b7444fe
more adjusts
johnatasr Oct 27, 2023
e0cf846
removing logrus
johnatasr Oct 27, 2023
b4a2ec2
adjust for message
johnatasr Oct 27, 2023
7fabcf8
adjust for message
johnatasr Oct 27, 2023
81ff6cf
add template tag
johnatasr Oct 30, 2023
f6bd50d
add template
johnatasr Oct 31, 2023
4bcb215
add more changes
johnatasr Nov 1, 2023
2e6bc03
Merge remote-tracking branch 'upstream/master' into feat-racher-relea…
johnatasr Nov 14, 2023
aae5899
using regex for rancher txt files
johnatasr Nov 14, 2023
e3b512e
add docs
johnatasr Nov 15, 2023
9422eac
Update rancher.go
johnatasr Nov 16, 2023
01b8712
remove unsed commit validation
johnatasr Nov 17, 2023
d47811b
split dev deps
johnatasr Nov 17, 2023
9683343
update doc and requires
johnatasr Nov 17, 2023
28b4198
remove unsed api url
johnatasr Nov 17, 2023
1616d52
more fixes
johnatasr Nov 23, 2023
a8d361f
remove func contents
johnatasr Nov 23, 2023
04eb84f
more adjusts
johnatasr Nov 27, 2023
1b965e6
add line and content to min version
johnatasr Nov 28, 2023
0fae92f
add rancher images export in example
johnatasr Nov 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions cmd/rancher_release/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,76 @@ rancher_release label-issues -t v2.8.1-rc1 --dry-run
# [Waiting for RC] -> [To Test]
```

### check-rancher-rc-deps

This command checks Rancher verifying if contains 'rc' and 'dev' dependencies for some files, this command could be used locally or remotely by commit hash. It generates an MD-formatted file print that can be used as a release description. If necessary, the command can generate an error if these dependencies are found, ideal for use in CI pipelines.

The pattern of files to be checked includes:
- `pkg/settings/setting.go`
- `package/Dockerfile`
- `scripts/package-env`
- `Dockerfile.dapper`
- `go.mod`
- `pkg/apis/go.mod`
- `pkg/client/go.mod`

| **Flag** | **Description** | **Required** |
| ---------------- | ----------------------------------------------------------------------------------------------------- | ------------ |
| `commit`, `c` | Commit used to get all files during the check, required for remote execution | FALSE |
| `org`, `o` | Reference organization of the commit, as default `rancher` | FALSE |
| `repo`, `r` | Reference repository of the commit, as default `rancher` | FALSE |
| `files`, `f` | List of files to be checked by the command, required for remote execution | TRUE |
| `for-ci`, `p` | With this flag, it's possible to return an error if any of the files contain 'rc' tags or 'dev' dependencies, ideal for use in integration pipelines | FALSE |

**Examples**
LOCAL
```
rancher_release check-rancher-rc-deps
```
REMOTE
```
rancher_release check-rancher-rc-deps -c <HASH_COMMIT> -f Dockerfile.dapper,go.mod,/package/Dockerfile,/pkg/apis/go.mod,/pkg/settings/setting.go,/scripts/package-env
```

```
# Components with -rc

* github.com/opencontainers/image-spec => github.com/opencontainers/image-spec v1.1.0-rc2 // needed for containers/image/v5 (go.mod, line 15)
* github.com/rancher/aks-operator v1.2.0-rc4 (go.mod, line 111)
* github.com/rancher/dynamiclistener v0.3.6-rc3-deadlock-fix-revert (go.mod, line 114)
* github.com/rancher/eks-operator v1.3.0-rc3 (go.mod, line 115)
* github.com/rancher/gke-operator v1.2.0-rc2 (go.mod, line 117)
* github.com/rancher/rke v1.5.0-rc5 (go.mod, line 124)
* github.com/opencontainers/image-spec v1.1.0-rc3 // indirect (go.mod, line 370)
* ENV CATTLE_RANCHER_WEBHOOK_VERSION=103.0.0+up0.4.0-rc9 (/package/Dockerfile, line 26)
* ENV CATTLE_CSP_ADAPTER_MIN_VERSION=103.0.0+up3.0.0-rc1 (/package/Dockerfile, line 27)
* ENV CATTLE_CLI_VERSION v2.8.0-rc1 (/package/Dockerfile, line 48)
* github.com/rancher/aks-operator v1.2.0-rc4 (/pkg/apis/go.mod, line 11)
* github.com/rancher/eks-operator v1.3.0-rc3 (/pkg/apis/go.mod, line 12)
* github.com/rancher/gke-operator v1.2.0-rc2 (/pkg/apis/go.mod, line 14)
* github.com/rancher/rke v1.5.0-rc5 (/pkg/apis/go.mod, line 16)
* ShellImage = NewSetting("shell-image", "rancher/shell:v0.1.21-rc1") (/pkg/settings/setting.go, line 121)

# Min version components with -rc

* ENV CATTLE_FLEET_MIN_VERSION=103.1.0+up0.9.0-rc.3
* ENV CATTLE_CSP_ADAPTER_MIN_VERSION=103.0.0+up3.0.0-rc1

# KDM References with dev branch

* ENV CATTLE_KDM_BRANCH=dev-v2.8 (Dockerfile.dapper, line 16)
* ARG CATTLE_KDM_BRANCH=dev-v2.8 (/package/Dockerfile, line 1)
* KDMBranch = NewSetting("kdm-branch", "dev-v2.8") (/pkg/settings/setting.go, line 84)

# Chart References with dev branch

* ARG SYSTEM_CHART_DEFAULT_BRANCH=dev-v2.8 (/package/Dockerfile, line 1)
* ARG CHART_DEFAULT_BRANCH=dev-v2.8 (/package/Dockerfile, line 1)
* ChartDefaultBranch = NewSetting("chart-default-branch", "dev-v2.8") (/pkg/settings/setting.go, line 116)
* SYSTEM_CHART_DEFAULT_BRANCH=${SYSTEM_CHART_DEFAULT_BRANCH:-"dev-v2.8"} (/scripts/package-env, line 5)
* CHART_DEFAULT_BRANCH=${CHART_DEFAULT_BRANCH:-"dev-v2.8"} (/scripts/package-env, line 7)
```

## Contributions

- File Issue with details of the problem, feature request, etc.
Expand Down
83 changes: 83 additions & 0 deletions cmd/rancher_release/check_rancher_rc_deps.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package main

import (
"context"
"errors"

"github.com/rancher/ecm-distro-tools/release/rancher"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
)

func checkRancherRCDepsCommand() *cli.Command {
return &cli.Command{
Name: "check-rancher-rc-deps",
Usage: "check if the Rancher version specified by the commit or pre-release title does not contain development dependencies and rc tags",
Flags: []cli.Flag{
&cli.StringFlag{
johnatasr marked this conversation as resolved.
Show resolved Hide resolved
Name: "commit",
Aliases: []string{"c"},
Usage: "last commit for a final rc",
Required: false,
johnatasr marked this conversation as resolved.
Show resolved Hide resolved
},
&cli.StringFlag{
johnatasr marked this conversation as resolved.
Show resolved Hide resolved
Name: "org",
Aliases: []string{"o"},
Usage: "organization name",
Required: false,
Value: "rancher",
},
&cli.StringFlag{
Name: "repo",
Aliases: []string{"r"},
Usage: "rancher repository",
Required: false,
Value: "rancher",
},
&cli.StringFlag{
johnatasr marked this conversation as resolved.
Show resolved Hide resolved
Name: "files",
Aliases: []string{"f"},
Usage: "files to be checked if remotely",
Required: false,
},
&cli.BoolFlag{
Name: "for-ci",
Aliases: []string{"p"},
Usage: "export a md template also check raising an error if contains rc tags and dev deps",
Required: false,
},
},
Action: checkRancherRCDeps,
}
}

func checkRancherRCDeps(c *cli.Context) error {
const files = "/bin/rancher-images.txt,/bin/rancher-windows-images.txt,Dockerfile.dapper,go.mod,/package/Dockerfile,/pkg/apis/go.mod,/pkg/settings/setting.go,/scripts/package-env"

var local bool

rcCommit := c.String("commit")
rcOrg := c.String("org")
rcRepo := c.String("repo")
rcFiles := c.String("files")
forCi := c.Bool("for-ci")

if rcCommit == "" {
if rcFiles != "" {
return errors.New("'commit hash' are required for remote operation")
}
}
if rcFiles == "" {
rcFiles = files
local = true
}

logrus.Debugf("organization: %s, repository: %s, commit: %s, files: %s",
rcOrg, rcRepo, rcCommit, rcFiles)

err := rancher.CheckRancherRCDeps(context.Background(), local, forCi, rcOrg, rcRepo, rcCommit, rcFiles)
if err != nil {
return err
}
return nil
}
1 change: 1 addition & 0 deletions cmd/rancher_release/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func main() {
checkRancherImageCommand(),
setKDMBranchReferencesCommand(),
setChartsBranchReferencesCommand(),
checkRancherRCDepsCommand(),
labelIssuesCommand(),
}
app.Flags = rootFlags
Expand Down
160 changes: 160 additions & 0 deletions release/rancher/rancher.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@ package rancher

import (
"bufio"
"bytes"
"context"
"errors"
"fmt"
"io"
"net/http"
"os"
"regexp"
"strconv"
"strings"
"text/template"
"time"

"github.com/google/go-github/v39/github"
Expand Down Expand Up @@ -102,6 +107,33 @@ if [ "${DRY_RUN}" = false ]; then
fi`
)

const templateCheckRCDevDeps = `{{- define "componentsFile" -}}
# Images with -rc
{{range .RancherImages}}
* {{ .Content }} ({{ .File }}, line {{ .Line }})
{{- end}}

# Components with -rc
{{range .FilesWithRC}}
* {{ .Content }} ({{ .File }}, line {{ .Line }})
{{- end}}

# Min version components with -rc
{{range .MinFilesWithRC}}
* {{ .Content }}
{{- end}}

# KDM References with dev branch
{{range .KDMWithDev}}
* {{ .Content }} ({{ .File }}, line {{ .Line }})
{{- end}}

# Chart References with dev branch
{{range .ChartsWithDev}}
* {{ .Content }} ({{ .File }}, line {{ .Line }})
{{- end}}
{{ end }}`

type SetBranchReferencesArgs struct {
RancherRepoPath string
NewBranch string
Expand Down Expand Up @@ -296,3 +328,131 @@ func createPRFromRancher(ctx context.Context, rancherBaseBranch, title, branchNa

return err
}

type ContentLine struct {
Line int
File string
Content string
}

type Content struct {
RancherImages []ContentLine
FilesWithRC []ContentLine
MinFilesWithRC []ContentLine
ChartsWithDev []ContentLine
KDMWithDev []ContentLine
}

func CheckRancherRCDeps(ctx context.Context, local, forCi bool, org, repo, commitHash, files string) error {
var (
content Content
badFiles bool
)

devDependencyPattern := regexp.MustCompile(`dev-v[0-9]+\.[0-9]+`)
rcTagPattern := regexp.MustCompile(`-rc[0-9]+`)

ghClient := repository.NewGithub(ctx, "")

for _, filePath := range strings.Split(files, ",") {
var scanner *bufio.Scanner
if local {
content, err := contentLocal(filePath)
if err != nil {
if os.IsNotExist(err) {
logrus.Debugf("file '%s' not found, skipping...", filePath)
continue
}
return err
}
defer content.Close()
scanner = bufio.NewScanner(content)
} else {
content, err := contentRemote(ctx, ghClient, org, repo, commitHash, filePath)
if err != nil {
return err
}
scanner = bufio.NewScanner(strings.NewReader(content))
}

lineNum := 1

for scanner.Scan() {
line := scanner.Text()
if devDependencyPattern.MatchString(line) {
lineContent := ContentLine{File: filePath, Line: lineNum, Content: formatContentLine(line)}
lineContentLower := strings.ToLower(lineContent.Content)
if strings.Contains(lineContentLower, "chart") {
badFiles = true
content.ChartsWithDev = append(content.ChartsWithDev, lineContent)
}
if strings.Contains(lineContentLower, "kdm") {
badFiles = true
content.KDMWithDev = append(content.KDMWithDev, lineContent)
}
}
if strings.Contains(filePath, "/package/Dockerfile") {
if !strings.Contains(line, "_VERSION") {
continue
}
tashima42 marked this conversation as resolved.
Show resolved Hide resolved
matches := regexp.MustCompile(`CATTLE_(\S+)_MIN_VERSION`).FindStringSubmatch(line)
if len(matches) == 2 && strings.Contains(line, "-rc") {
johnatasr marked this conversation as resolved.
Show resolved Hide resolved
badFiles = true
lineContent := ContentLine{Line: lineNum, File: filePath, Content: formatContentLine(line)}
content.MinFilesWithRC = append(content.MinFilesWithRC, lineContent)
}
}
if rcTagPattern.MatchString(line) {
badFiles = true
lineContent := ContentLine{File: filePath, Line: lineNum, Content: formatContentLine(line)}
content.FilesWithRC = append(content.FilesWithRC, lineContent)
}
lineNum++
}
if err := scanner.Err(); err != nil {
return err
}
}

tmpl := template.New("rancher-release-rc-dev-deps")
tmpl = template.Must(tmpl.Parse(templateCheckRCDevDeps))
buff := bytes.NewBuffer(nil)
err := tmpl.ExecuteTemplate(buff, "componentsFile", content)
if err != nil {
return err
}

fmt.Println(buff.String())

if forCi && badFiles {
return errors.New("check failed, some files don't match the expected dependencies for a final release candidate")
}

return nil
}

func contentLocal(filePath string) (*os.File, error) {
repoContent, err := os.Open(filePath)
if err != nil {
return nil, err
}
return repoContent, nil
}

func contentRemote(ctx context.Context, ghClient *github.Client, org, repo, commitHash, filePath string) (string, error) {
content, _, _, err := ghClient.Repositories.GetContents(ctx, org, repo, filePath, &github.RepositoryContentGetOptions{Ref: commitHash})
if err != nil {
return "", err
}
decodedContent, err := content.GetContent()
if err != nil {
return "", err
}
return decodedContent, nil
}

func formatContentLine(line string) string {
re := regexp.MustCompile(`\s+`)
line = re.ReplaceAllString(line, " ")
return strings.TrimSpace(line)
}
5 changes: 5 additions & 0 deletions repository/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const (
emptyReleaseNote = "```release-note\r\n\r\n```"
noneReleaseNote = "```release-note\r\nNONE\r\n```"
httpTimeout = time.Second * 10
ghContentURL = "https://raw.githubusercontent.com"
)

// stripBackportTag returns a string with a prefix backport tag removed
Expand Down Expand Up @@ -50,6 +51,10 @@ func (t *TokenSource) Token() (*oauth2.Token, error) {
// NewGithub creates a value of type github.Client pointer
// with the given context and Github token.
func NewGithub(ctx context.Context, token string) *github.Client {
if token == "" {
return github.NewClient(nil)
}

ts := TokenSource{
AccessToken: token,
}
Expand Down