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

add the generate release announcement message command #483

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
6 changes: 3 additions & 3 deletions cmd/release/cmd/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ var rancherGenerateReleaseMessageSubCmd = &cobra.Command{

rancherRelease, found := rootConfig.Rancher.Versions[versionKey]
if !found {
return errors.New("verify your config file, version not found: " + rancherReleaseAnnouncementTag)
return errors.New("verify your config file, version not found: " + versionKey)
}

ctx := context.Background()
Expand All @@ -199,7 +199,7 @@ var rancherGenerateReleaseMessageSubCmd = &cobra.Command{
if err != nil {
return err
}
fmt.Print(message)
fmt.Println(message)
return nil
},
}
Expand Down Expand Up @@ -298,7 +298,7 @@ func init() {
rancherGenerateReleaseMessageSubCmd.Flags().StringVarP(&rancherReleaseAnnouncementTag, "tag", "t", "", "Tag that will be announced")
rancherGenerateReleaseMessageSubCmd.Flags().StringVarP(&rancherReleaseAnnouncementPreviousTag, "previous-tag", "p", "", "Last tag before the current one")
rancherGenerateReleaseMessageSubCmd.Flags().StringVarP(&rancherReleaseAnnouncementActionRunID, "action-run-id", "a", "", "Run ID for the latest push-release.yml action")
rancherGenerateReleaseMessageSubCmd.Flags().BoolVarP(&rancherReleaseAnnouncementPrimeOnly, "prime-only", "p", false, "Version is prime-only and the artifacts are at prime.ribs.rancher.io")
rancherGenerateReleaseMessageSubCmd.Flags().BoolVarP(&rancherReleaseAnnouncementPrimeOnly, "prime-only", "o", false, "Version is prime-only and the artifacts are at prime.ribs.rancher.io")
if err := rancherGenerateReleaseMessageSubCmd.MarkFlagRequired("tag"); err != nil {
fmt.Println(err.Error())
os.Exit(1)
Expand Down
125 changes: 104 additions & 21 deletions release/rancher/rancher.go
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ func GenerateMissingImagesList(imagesListURL, registry string, concurrencyLimit
if imagesListURL == "" {
return nil, errors.New("if no images are provided, an images list URL must be provided")
}
rancherImages, err := rancherPrimeArtifact(imagesListURL)
rancherImages, err := remoteTextFileToSlice(imagesListURL)
if err != nil {
return nil, errors.New("failed to get rancher images: " + err.Error())
}
Expand Down Expand Up @@ -671,34 +671,50 @@ func GenerateAnnounceReleaseMessage(ctx context.Context, ghClient *github.Client
return "", errors.New("release commit sha is nil")
}

isPreRelease := strings.ContainsRune(tag, '-')
// if preRelease
// get the rancher-components.txt artifact and extract images with RC and components with RC
// else
// go to rancher/rancher/package/Dockerfile and get the UI and CLI versions

r := releaseAnnnouncement{
Tag: tag,
PreviousTag: previousTag,
RancherRepoOwner: rancherRepoOwner,
CommitSHA: *release.TargetCommitish,
ActionRunID: actionRunID,
ImagesWithRC: []string{
"rancher/aks-operator v1.9.2-rc.1",
"rancher/cis-operator v1.0.15-rc.2",
},
ComponentsWithRC: []string{
"SYSTEM_AGENT_VERSION v0.3.9-rc.4",
"WINS_AGENT_VERSION v0.4.18-rc2",
},
UIVersion: "v2.9.2-ui",
CLIVersion: "v2.9.2-cli",
}

announceTemplate := announceReleaseGATemplate

// only prerelease images like alpha, rc or hotfix contain - (e.g: v2.9.2-alpha1)
isPreRelease := strings.ContainsRune(tag, '-')
if isPreRelease {
announceTemplate = announceReleasePreReleaseTemplate

componentsURL := "https://github.com/" + rancherRepoOwner + "/rancher/releases/download/" + tag + "/rancher-components.txt"
if primeOnly {
componentsURL = rancherArtifactsBaseURL + "/rancher/" + tag + "/rancher-components.txt"
}
rancherComponents, err := remoteTextFileToSlice(componentsURL)
if err != nil {
return "", err
}

imagesWithRC, componentsWithRC, err := rancherImagesComponentsWithRC(rancherComponents)
if err != nil {
return "", err
}
r.ImagesWithRC = imagesWithRC
r.ComponentsWithRC = componentsWithRC
} else {
dockerfileURL := "https://raw.githubusercontent.com/" + rancherRepoOwner + "/rancher/" + *release.TargetCommitish + "/package/Dockerfile"
dockerfile, err := remoteTextFileToSlice(dockerfileURL)
if err != nil {
return "", err
}
uiVersion, cliVersion, err := rancherUICLIVersions(dockerfile)
if err != nil {
return "", err
}
r.UIVersion = uiVersion
r.CLIVersion = cliVersion
}

// only pre release versions contain a suffix that starts with "-" (v2.9.2-alpha1)
tmpl := template.New("announce-release")
tmpl, err = tmpl.Parse(announceTemplate)
Expand All @@ -712,6 +728,70 @@ func GenerateAnnounceReleaseMessage(ctx context.Context, ghClient *github.Client
return buff.String(), nil
}

// rancherUICLIVersions scans a dockerfile line by line and retruns the ui and cli versions, or an error if any of them are not found
func rancherUICLIVersions(dockerfile []string) (string, string, error) {
var uiVersion string
var cliVersion string
for _, line := range dockerfile {
if strings.Contains(line, "ENV CATTLE_UI_VERSION ") {
uiVersion = strings.TrimPrefix(line, "ENV CATTLE_UI_VERSION ")
continue
}
if strings.Contains(line, "ENV CATTLE_CLI_VERSION ") {
cliVersion = strings.TrimPrefix(line, "ENV CATTLE_CLI_VERSION ")
continue
}
if len(uiVersion) > 0 && len(cliVersion) > 0 {
break
}
}
if uiVersion == "" || cliVersion == "" {
return "", "", errors.New("missing ui or cli version")
}
return uiVersion, cliVersion, nil
}

// rancherImagesComponentsWithRC scans the rancher-components.txt file content and returns images and components, or an error
func rancherImagesComponentsWithRC(rancherComponents []string) ([]string, []string, error) {
if len(rancherComponents) < 2 {
return nil, nil, errors.New("rancher-components.txt should have at least two lines (images and components headers)")
}
images := make([]string, 0)
components := make([]string, 0)

var isImage bool
for _, line := range rancherComponents {
// always skip empty lines
if line == "" || line == " " {
continue
}

// if a line contains # it is a header for a section
isHeader := strings.Contains(line, "#")
imagesHeader := strings.Contains(line, "Images")
componentsHeader := strings.Contains(line, "Components")

if isHeader {
// if it's a header, but not for images or components, ignore it and everything else after it
if !imagesHeader && !componentsHeader {
break
}
// isImage's value will persist between iterations
// if imagesHeader is true, it means that all following lines are images
// if it's false, it means that all following images are components
isImage = imagesHeader
continue
}

if isImage {
images = append(images, line)
} else {
components = append(components, line)
}
}
return images, components, nil
}

func dockerImagesDigests(imagesFileURL, registry string) (imageDigest, error) {
imagesList, err := artifactImageList(imagesFileURL, registry)
if err != nil {
Expand Down Expand Up @@ -887,7 +967,7 @@ func registryAuth(authURL, service, image string) (string, error) {
defer res.Body.Close()

if res.StatusCode != http.StatusOK {
return "", errors.New("expected status code to be 200, got: " + strconv.Itoa(res.StatusCode))
return "", errors.New("expected status code to be 200, got: " + res.Status)
}

var auth registryAuthToken
Expand All @@ -898,12 +978,15 @@ func registryAuth(authURL, service, image string) (string, error) {
return auth.Token, nil
}

func rancherPrimeArtifact(url string) ([]string, error) {
func remoteTextFileToSlice(url string) ([]string, error) {
httpClient := ecmHTTP.NewClient(time.Second * 15)
res, err := httpClient.Get(url)
if err != nil {
return nil, err
}
if res.StatusCode != 200 {
return nil, errors.New("expected status code to be 200, got: " + res.Status)
}
defer res.Body.Close()

var file []string
Expand Down Expand Up @@ -1034,5 +1117,5 @@ const announceReleasePreReleaseTemplate = `{{ define "announceRelease" }}` + ann
" * {{ . }}\n{{ end }}{{ end }}"

const announceReleaseGATemplate = `{{ define "announceRelease" }}` + announceReleaseHeaderTemplate +
"* UI Version: {{ .UIVersion }}\n" +
"* CLI Version: {{ .CLIVersion }}{{ end }}"
"* UI Version: `{{ .UIVersion }}`\n" +
"* CLI Version: `{{ .CLIVersion }}`{{ end }}"
66 changes: 65 additions & 1 deletion release/rancher/rancher_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package rancher

import "testing"
import (
"testing"
)

const (
rancherRepoImage = "rancher/rancher"
Expand Down Expand Up @@ -86,3 +88,65 @@ func TestGenerateRegsyncConfig(t *testing.T) {
t.Error("rancher agent image should be: '" + sourceRancherAgentImage + "' instead, got: '" + config.Sync[1].Source + "'")
}
}

func TestRancherUICLIVersions(t *testing.T) {
ui := "2.9.2-alpha3"
cli := "v2.9.0"
dockerfile := []string{
"empty line",
"ENV CATTLE_UI_VERSION " + ui,
"ENV CATTLE_DASHBOARD_UI_VERSION v2.9.2-alpha3",
"ENV CATTLE_CLI_VERSION " + cli,
"",
"another empty line",
}
uiVersion, cliVersion, err := rancherUICLIVersions(dockerfile)
if err != nil {
t.Error(err)
}
if uiVersion != ui {
t.Error("wrong ui version, expected '" + ui + "', instead, got: " + uiVersion)
}
if cliVersion != cli {
t.Error("wrong cli version, expected '" + cli + "', instead, got: " + cliVersion)
}
}

func TestRancherImagesComponentsWithRC(t *testing.T) {
cisOperatorImage := "rancher/cis-operator v1.0.15-rc.2"
fleetImage := "rancher/fleet v0.9.9-rc.1"
systemAgentComponent := "SYSTEM_AGENT_VERSION v0.3.9-rc.4"
winsAgentComponent := "WINS_AGENT_VERSION v0.4.18-rc1"

rancherComponents := []string{
"# Images with -rc",
cisOperatorImage,
fleetImage,
"# Components with -rc",
systemAgentComponent,
winsAgentComponent,
"",
"# Min version components with -rc",
"",
"# Chart/KDM sources",
"* SYSTEM_CHART_DEFAULT_BRANCH: dev-v2.8 (`scripts/package-env`)",
}

images, components, err := rancherImagesComponentsWithRC(rancherComponents)
if err != nil {
t.Error(err)
}

if images[0] != cisOperatorImage {
t.Error("image mismatch, expected '" + cisOperatorImage + "', instead, got: " + images[0])
}
if images[1] != fleetImage {
t.Error("image mismatch, expected '" + fleetImage + "', instead, got: " + images[1])
}
if components[0] != systemAgentComponent {
t.Error("image mismatch, expected '" + systemAgentComponent + "', instead, got: " + components[0])
}
if components[1] != winsAgentComponent {
t.Error("image mismatch, expected '" + winsAgentComponent + "', instead, got: " + components[1])
}
}
Loading