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

Update to allowing building and publishing images in hashrelease #9429

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ node/windows-packaging/nssm.exe
_output
builder.coverprofile
*.log
.release-*.*

/* Created by local kind cluster */
hack/test/kind/kind
Expand Down
6 changes: 4 additions & 2 deletions .semaphore/release/hashrelease.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,14 @@ blocks:
jobs:
- name: Build and publish hashrelease
commands:
- |
if [[ ${SEMAPHORE_PIPELINE_PROMOTION} == "true" ]]; then export BUILD_IMAGES=true; export SKIP_PUBLISH_IMAGES=false; fi
- make hashrelease
prologue:
commands:
- export GITHUB_TOKEN=${MARVIN_GITHUB_TOKEN}
- cd release
- make build
env_vars:
- name: IS_HASHRELEASE
value: "true"
- name: REGISTRIES
value: docker.io/calico
21 changes: 21 additions & 0 deletions key-cert-provisioner/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ KEY_CERT_PROVISIONER_CREATED=.key-cert-provisioner.created-$(ARCH)-fips
VALIDARCHES=amd64
BINDIR=bin/$(ARCH)-fips
LATEST_TAG=latest-fips
BUILD_IMAGES=$(KEY_CERT_PROVISIONER_IMAGE)
PUSH_IMAGES= $(foreach registry,$(DEV_REGISTRIES),$(addprefix $(registry)/,$(BUILD_IMAGES)))
RELEASE_IMAGES= $(foreach registry,$(RELEASE_REGISTRIES),$(addprefix $(registry)/,$(BUILD_IMAGES)))
else
KEY_CERT_PROVISIONER_CREATED=.key-cert-provisioner.created-$(ARCH)
BINDIR=bin
Expand Down Expand Up @@ -93,3 +96,21 @@ clean:
-docker image rm -f $$(docker images $(KEY_CERT_PROVISIONER_IMAGE) -a -q)
-docker image rm -f $$(docker images $(TEST_SIGNER_IMAGE) -a -q)

###############################################################################
# Release
###############################################################################
release-build: .release-$(VERSION).created
.release-$(VERSION).created:
$(MAKE) clean image-all RELEASE=true
$(MAKE) retag-build-images-with-registries RELEASE=true IMAGETAG=$(VERSION)
$(MAKE) retag-build-images-with-registries RELEASE=true IMAGETAG=latest
$(MAKE) FIPS=true retag-build-images-with-registries RELEASE=true IMAGETAG=$(VERSION)-fips LATEST_IMAGE_TAG=latest-fips
$(MAKE) FIPS=true retag-build-images-with-registries RELEASE=true IMAGETAG=latest-fips LATEST_IMAGE_TAG=latest-fips
touch $@

## Pushes a github release and release artifacts produced by `make release-build`.
release-publish: release-prereqs .release-$(VERSION).published
.release-$(VERSION).published:
$(MAKE) push-images-to-registries IMAGETAG=$(VERSION) RELEASE=$(RELEASE) CONFIRM=$(CONFIRM)
$(MAKE) FIPS=true push-images-to-registries IMAGETAG=$(VERSION)-fips RELEASE=$(RELEASE) CONFIRM=$(CONFIRM)
touch $@
102 changes: 74 additions & 28 deletions release/build/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"gopkg.in/natefinch/lumberjack.v2"

"github.com/projectcalico/calico/release/internal/config"
"github.com/projectcalico/calico/release/internal/hashreleaseserver"
"github.com/projectcalico/calico/release/internal/outputs"
"github.com/projectcalico/calico/release/internal/pinnedversion"
"github.com/projectcalico/calico/release/internal/registry"
Expand All @@ -37,12 +38,13 @@ import (
)

const (
latestFlag = "latest"
skipValidationFlag = "skip-validation"
skipImageScanFlag = "skip-image-scan"
skipBranchCheckFlag = "skip-branch-check"
publishBranchFlag = "git-publish"
buildImagesFlag = "build-images"
latestFlag = "latest"
skipValidationFlag = "skip-validation"
skipImageScanFlag = "skip-image-scan"
skipBranchCheckFlag = "skip-branch-check"
skipReleaseNotesFlag = "skip-release-notes"
publishBranchFlag = "git-publish"
buildImagesFlag = "build-images"

orgFlag = "org"
repoFlag = "repo"
Expand All @@ -58,9 +60,10 @@ const (
newBranchFlag = "new-branch-version"

// Configuration flags for the release publish command.
skipPublishImagesFlag = "skip-publish-images"
skipPublishGitTag = "skip-publish-git-tag"
skipPublishGithubRelease = "skip-publish-github-release"
skipPublishImagesFlag = "skip-publish-images"
skipPublishGitTagFlag = "skip-publish-git-tag"
skipPublishGithubReleaseFlag = "skip-publish-github-release"
skipPublishHashreleaseFlag = "skip-publish-hashrelease-server"
)

var (
Expand Down Expand Up @@ -159,8 +162,9 @@ func hashreleaseSubCommands(cfg *config.Config) []*cli.Command {
&cli.StringFlag{Name: repoFlag, Usage: "Git repository", EnvVars: []string{"GIT_REPO"}, Value: config.DefaultRepo},
&cli.BoolFlag{Name: skipValidationFlag, Usage: "Skip all pre-build validation", Value: false},
&cli.BoolFlag{Name: skipBranchCheckFlag, Usage: "Skip check that this is a valid release branch.", Value: false},
&cli.BoolFlag{Name: buildImagesFlag, Usage: "Build images from local codebase. If false, will use images from CI instead.", Value: false},
&cli.StringFlag{Name: imageRegistryFlag, Usage: "Specify image registry to use", Value: ""},
&cli.BoolFlag{Name: skipReleaseNotesFlag, Usage: "Skip generating release notes. For dev, you want to set to true", Value: false},
&cli.BoolFlag{Name: buildImagesFlag, Usage: "Build images from local codebase. If false, will use images from CI instead.", EnvVars: []string{"BUILD_IMAGES"}, Value: false},
&cli.StringFlag{Name: imageRegistryFlag, Usage: "Specify image registry to use", EnvVars: []string{"REGISTRIES"}, Value: ""},
&cli.StringFlag{Name: operatorOrgFlag, Usage: "Operator git organization", EnvVars: []string{"OPERATOR_GIT_ORGANIZATION"}, Value: config.OperatorDefaultOrg},
&cli.StringFlag{Name: operatorRepoFlag, Usage: "Operator git repository", EnvVars: []string{"OPERATOR_GIT_REPO"}, Value: config.OperatorDefaultRepo},
&cli.StringFlag{Name: operatorImageFlag, Usage: "Specify the operator image to use", EnvVars: []string{"OPERATOR_IMAGE"}, Value: config.OperatorDefaultImage},
Expand Down Expand Up @@ -198,6 +202,7 @@ func hashreleaseSubCommands(cfg *config.Config) []*cli.Command {
RootDir: cfg.RepoRootDir,
ReleaseBranchPrefix: cfg.RepoReleaseBranchPrefix,
Operator: cfg.Operator,
Registry: c.String(imageRegistryFlag),
}
if c.String(operatorImageFlag) != "" {
pinnedCfg.Operator.Image = c.String(operatorImageFlag)
Expand Down Expand Up @@ -268,10 +273,12 @@ func hashreleaseSubCommands(cfg *config.Config) []*cli.Command {
return err
}

// For real releases, release notes are generated prior to building the release. For hash releases,
// generate a set of release notes and add them to the hashrelease directory.
if _, err := outputs.ReleaseNotes(c.String(orgFlag), cfg.GithubToken, cfg.RepoRootDir, filepath.Join(dir, releaseNotesDir), versions.ProductVersion); err != nil {
return err
if !c.Bool(skipReleaseNotesFlag) {
// For real releases, release notes are generated prior to building the release.
// For hash releases, generate a set of release notes and add them to the hashrelease directory.
if _, err := outputs.ReleaseNotes(c.String(orgFlag), cfg.GithubToken, cfg.RepoRootDir, filepath.Join(dir, releaseNotesDir), versions.ProductVersion); err != nil {
return err
}
}

// Adjsut the formatting of the generated outputs to match the legacy hashrelease format.
Expand All @@ -284,6 +291,11 @@ func hashreleaseSubCommands(cfg *config.Config) []*cli.Command {
Name: "publish",
Usage: "Publish hashrelease from _output/ to hashrelease server",
Flags: []cli.Flag{
&cli.StringFlag{Name: orgFlag, Usage: "Git organization", EnvVars: []string{"ORGANIZATION"}, Value: config.DefaultOrg},
&cli.StringFlag{Name: repoFlag, Usage: "Git repository", EnvVars: []string{"GIT_REPO"}, Value: config.DefaultRepo},
&cli.StringFlag{Name: imageRegistryFlag, Usage: "Specify image registry to use", EnvVars: []string{"REGISTRIES"}, Value: ""},
&cli.BoolFlag{Name: skipPublishImagesFlag, Usage: "Skip publishing of container images to registry", EnvVars: []string{"SKIP_PUBLISH_IMAGES"}, Value: true},
&cli.BoolFlag{Name: skipPublishHashreleaseFlag, Usage: "Skip publishing to hashrelease server", Value: false},
&cli.BoolFlag{Name: latestFlag, Usage: "Promote this release as the latest for this stream", Value: true},
&cli.BoolFlag{Name: skipValidationFlag, Usage: "Skip pre-build validation", Value: false},
&cli.BoolFlag{Name: skipImageScanFlag, Usage: "Skip sending images to image scan service.", Value: false},
Expand All @@ -297,17 +309,20 @@ func hashreleaseSubCommands(cfg *config.Config) []*cli.Command {
return fmt.Errorf("%s must be set if %s is set", skipImageScanFlag, skipValidationFlag)
}

// Extract the version from pinned-version.yaml.
hash, err := pinnedversion.RetrievePinnedVersionHash(cfg.TmpFolderPath())
// Extract the pinned version as a hashrelease.
hashrel, err := pinnedversion.LoadHashrelease(cfg.RepoRootDir, cfg.TmpFolderPath(), dir)
if err != nil {
return err
}
if c.Bool(latestFlag) {
hashrel.Latest = true
}

// Check if the hashrelease has already been published.
if published, err := tasks.HashreleasePublished(cfg, hash); err != nil {
if published, err := tasks.HashreleasePublished(cfg, hashrel.Hash); err != nil {
return err
} else if published {
return fmt.Errorf("hashrelease %s has already been published", hash)
return fmt.Errorf("%s hashrelease (%s) has already been published", hashrel.Name, hashrel.Hash)
}

// Push the operator hashrelease first before validaion
Expand All @@ -321,10 +336,38 @@ func hashreleaseSubCommands(cfg *config.Config) []*cli.Command {
if err := o.Publish(cfg.TmpFolderPath()); err != nil {
return err
}
if !c.Bool(skipValidationFlag) {
tasks.HashreleaseValidate(cfg, c.Bool(skipImageScanFlag))

opts := []calico.Option{
calico.WithRepoRoot(cfg.RepoRootDir),
calico.IsHashRelease(),
calico.WithVersions(&version.Data{
ProductVersion: version.New(hashrel.ProductVersion),
OperatorVersion: version.New(hashrel.OperatorVersion),
}),
calico.WithGithubOrg(c.String(orgFlag)),
calico.WithRepoName(c.String(repoFlag)),
calico.WithRepoRemote(cfg.GitRemote),
calico.WithValidate(!c.Bool(skipValidationFlag)),
calico.WithTmpDir(cfg.TmpFolderPath()),
calico.WithHashrelease(*hashrel, cfg.HashreleaseServerConfig),
calico.WithPublishImages(!c.Bool(skipPublishImagesFlag)),
calico.WithPublishHashrelease(!c.Bool(skipPublishHashreleaseFlag)),
calico.WithImageScanning(!c.Bool(skipImageScanFlag), cfg.ImageScannerConfig),
}
if reg := c.String(imageRegistryFlag); reg != "" {
opts = append(opts, calico.WithImageRegistries([]string{reg}))
}
r := calico.NewManager(opts...)
if err := r.PublishRelease(); err != nil {
return err
}

// Send a slack message to notify that the hashrelease has been published.
if !c.Bool(skipPublishHashreleaseFlag) {
if err := tasks.HashreleaseSlackMessage(cfg, hashrel); err != nil {
return err
}
}
tasks.HashreleasePush(cfg, dir, c.Bool(latestFlag))
return nil
},
},
Expand All @@ -336,8 +379,7 @@ func hashreleaseSubCommands(cfg *config.Config) []*cli.Command {
Aliases: []string{"gc"},
Action: func(c *cli.Context) error {
configureLogging("hashrelease-garbage-collect.log")
tasks.HashreleaseCleanRemote(cfg)
return nil
return hashreleaseserver.CleanOldHashreleases(&cfg.HashreleaseServerConfig)
},
},
}
Expand Down Expand Up @@ -379,6 +421,7 @@ func releaseSubCommands(cfg *config.Config) []*cli.Command {
Flags: []cli.Flag{
&cli.StringFlag{Name: orgFlag, Usage: "Git organization", EnvVars: []string{"ORGANIZATION"}, Value: config.DefaultOrg},
&cli.StringFlag{Name: repoFlag, Usage: "Git repository", EnvVars: []string{"GIT_REPO"}, Value: config.DefaultRepo},
&cli.BoolFlag{Name: buildImagesFlag, Usage: "Build images from local codebase. If false, will use images from CI instead.", EnvVars: []string{"BUILD_IMAGES"}, Value: true},
&cli.BoolFlag{Name: skipValidationFlag, Usage: "Skip pre-build validation", Value: false},
&cli.StringFlag{Name: imageRegistryFlag, Usage: "Specify image registry to use", Value: ""},
},
Expand Down Expand Up @@ -408,6 +451,7 @@ func releaseSubCommands(cfg *config.Config) []*cli.Command {
calico.WithGithubOrg(c.String(orgFlag)),
calico.WithRepoName(c.String(repoFlag)),
calico.WithRepoRemote(cfg.GitRemote),
calico.WithBuildImages(c.Bool(buildImagesFlag)),
}
if c.Bool(skipValidationFlag) {
opts = append(opts, calico.WithValidate(false))
Expand All @@ -427,9 +471,9 @@ func releaseSubCommands(cfg *config.Config) []*cli.Command {
Flags: []cli.Flag{
&cli.StringFlag{Name: orgFlag, Usage: "Git organization", EnvVars: []string{"ORGANIZATION"}, Value: config.DefaultOrg},
&cli.StringFlag{Name: repoFlag, Usage: "Git repository", EnvVars: []string{"GIT_REPO"}, Value: config.DefaultRepo},
&cli.BoolFlag{Name: skipPublishImagesFlag, Usage: "Skip publishing of container images to registry", Value: false},
&cli.BoolFlag{Name: skipPublishGitTag, Usage: "Skip publishing of tag to git repository", Value: false},
&cli.BoolFlag{Name: skipPublishGithubRelease, Usage: "Skip publishing of release to Github", Value: false},
&cli.BoolFlag{Name: skipPublishImagesFlag, Usage: "Skip publishing of container images to registry", EnvVars: []string{"SKIP_PUBLISH_IMAGES"}, Value: false},
&cli.BoolFlag{Name: skipPublishGitTagFlag, Usage: "Skip publishing of tag to git repository", Value: false},
&cli.BoolFlag{Name: skipPublishGithubReleaseFlag, Usage: "Skip publishing of release to Github", Value: false},
&cli.StringFlag{Name: imageRegistryFlag, Usage: "Specify image registry to use", Value: ""},
},
Action: func(c *cli.Context) error {
Expand All @@ -445,10 +489,12 @@ func releaseSubCommands(cfg *config.Config) []*cli.Command {
OperatorVersion: operatorVer,
}),
calico.WithOutputDir(filepath.Join(baseUploadDir, ver.FormattedString())),
calico.WithPublishOptions(!c.Bool(skipPublishImagesFlag), !c.Bool(skipPublishGitTag), !c.Bool(skipPublishGithubRelease)),
calico.WithGithubOrg(c.String(orgFlag)),
calico.WithRepoName(c.String(repoFlag)),
calico.WithRepoRemote(cfg.GitRemote),
calico.WithPublishImages(!c.Bool(skipPublishImagesFlag)),
calico.WithPublishGitTag(!c.Bool(skipPublishGitTagFlag)),
calico.WithPublishGithubRelease(!c.Bool(skipPublishGithubReleaseFlag)),
}
if reg := c.String(imageRegistryFlag); reg != "" {
opts = append(opts, calico.WithImageRegistries([]string{reg}))
Expand Down
4 changes: 2 additions & 2 deletions release/internal/hashreleaseserver/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ type Config struct {
KnownHosts string `envconfig:"DOCS_KNOWN_HOSTS"`
}

// rshVars returns the ssh command for rsync to use for the connection
func (s *Config) rshVars() string {
// RSHVars returns the ssh command for rsync to use for the connection
func (s *Config) RSHVars() string {
str := []string{"ssh", "-i", s.Key, "-p", s.Port, "-q", "-o StrictHostKeyChecking=yes"}
if s.KnownHosts != "" {
str = append(str, "-o UserKnownHostsFile="+s.KnownHosts)
Expand Down
40 changes: 16 additions & 24 deletions release/internal/hashreleaseserver/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ import (
"time"

"github.com/sirupsen/logrus"

"github.com/projectcalico/calico/release/internal/command"
)

const (
Expand All @@ -52,6 +50,12 @@ type Hashrelease struct {
// Stream is the version the hashrelease is for (e.g master, v3.19)
Stream string

// ProductVersion is the product version in the hashrelease
ProductVersion string

// OperatorVersion is the operator version for the hashreleaseq
OperatorVersion string

// Source is the source of hashrelease content
Source string

Expand All @@ -62,11 +66,11 @@ type Hashrelease struct {
Latest bool
}

func (h Hashrelease) URL() string {
func (h *Hashrelease) URL() string {
return fmt.Sprintf("https://%s.%s", h.Name, BaseDomain)
}

func remoteDocsPath(user string) string {
func RemoteDocsPath(user string) string {
path := "files"
if user != "root" {
path = filepath.Join("home", "core", "disk", "docs-preview", path)
Expand All @@ -75,7 +79,7 @@ func remoteDocsPath(user string) string {
}

func remoteReleasesLibraryPath(user string) string {
return filepath.Join(remoteDocsPath(user), "all-releases")
return filepath.Join(RemoteDocsPath(user), "all-releases")
}

func HasHashrelease(hash string, cfg *Config) bool {
Expand All @@ -86,25 +90,13 @@ func HasHashrelease(hash string, cfg *Config) bool {
return false
}

// PublishHashrelease publishes a hashrelease to the server
func PublishHashrelease(rel Hashrelease, cfg *Config) error {
logrus.WithFields(logrus.Fields{
"hashrelease": rel.Name,
"hash": rel.Hash,
"source": rel.Source,
}).Debug("Publishing hashrelease")
dir := rel.Source + "/"
if _, err := command.Run("rsync", []string{"--stats", "-az", "--delete", fmt.Sprintf("--rsh=%s", cfg.rshVars()), dir, fmt.Sprintf("%s:%s/%s", cfg.HostString(), remoteDocsPath(cfg.User), rel.Name)}); err != nil {
logrus.WithError(err).Error("Failed to publish hashrelease")
// SetHashreleaseAsLatest sets the hashrelease as the latest for the stream
func SetHashreleaseAsLatest(rel Hashrelease, cfg *Config) error {
logrus.Debugf("Updating latest hashrelease for %s stream to %s", rel.Stream, rel.Name)
if _, err := runSSHCommand(cfg, fmt.Sprintf(`echo "%s/" > %s/latest-os/%s.txt && echo %s >> %s`, rel.URL(), RemoteDocsPath(cfg.User), rel.Stream, rel.Name, remoteReleasesLibraryPath(cfg.User))); err != nil {
logrus.WithError(err).Error("Failed to update latest hashrelease and hashrelease library")
return err
}
if rel.Latest {
logrus.Debugf("Updating latest hashrelease for %s stream to %s", rel.Stream, rel.Name)
if _, err := runSSHCommand(cfg, fmt.Sprintf(`echo "%s/" > %s/latest-os/%s.txt && echo %s >> %s`, rel.URL(), remoteDocsPath(cfg.User), rel.Stream, rel.Name, remoteReleasesLibraryPath(cfg.User))); err != nil {
logrus.WithError(err).Error("Failed to update latest hashrelease and hashrelease library")
return err
}
}
return nil
}

Expand Down Expand Up @@ -136,7 +128,7 @@ func CleanOldHashreleases(cfg *Config) error {
}

func listHashreleases(cfg *Config) ([]Hashrelease, error) {
cmd := fmt.Sprintf("ls -lt --time-style=+'%%Y-%%m-%%d %%H:%%M:%%S' %s", remoteDocsPath(cfg.User))
cmd := fmt.Sprintf("ls -lt --time-style=+'%%Y-%%m-%%d %%H:%%M:%%S' %s", RemoteDocsPath(cfg.User))
out, err := runSSHCommand(cfg, cmd)
if err != nil {
logrus.WithError(err).Error("Failed to get list of hashreleases")
Expand All @@ -162,7 +154,7 @@ func listHashreleases(cfg *Config) ([]Hashrelease, error) {
}
if re.MatchString(name) {
releases = append(releases, Hashrelease{
Name: filepath.Join(remoteDocsPath(cfg.User), name),
Name: filepath.Join(RemoteDocsPath(cfg.User), name),
Time: time,
})
}
Expand Down
Loading