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

feat: add release markers #170

Merged
merged 5 commits into from
Mar 4, 2025
Merged
Show file tree
Hide file tree
Changes from 4 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
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,26 @@ The locking mechanism is based on the existance of a file in a defined `aws_s3_l

NOTE: Currently in case of an interrupted job, the lock file will not be removed and it would be necessary to remove it manually from the bucket.

## Release Markers

In order to track the releases made with the Publish Action, the action appends information about each execution in a file in the S3 bucket.
The file is persisted in the root of the repository (`/infrastructure-agent/`) and contains the following information:

```json
[
{
"app_name": "newrelic-infra",
"tag": "1.7.0",
"run_id": "13549016185",
"start": "2025-02-26T16:57:33Z",
"end": "2025-02-26T16:58:47Z",
"repo_name": "newrelic/infrastructure-agent",
"schema": "custom",
"schema_url": "https://raw.githubusercontent.com/newrelic/infrastructure-agent/test_publish_action_markers/build/upload-schema-linux-deb.yml"
}
]
```

## Support

If you need assistance with New Relic products, you are in good hands with several support diagnostic tools and support channels.
Expand Down
8 changes: 8 additions & 0 deletions publisher/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,16 @@ type Config struct {
ArtifactsDestFolder string // s3 mounted folder
ArtifactsSrcFolder string
AptlyFolder string
SchemaURL string
Schema string
Comment on lines +38 to +39
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added to the config so we can show this information in the release markersr.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since they were already provided in the action, no change is needed in the inputs, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. They were used only to process them, download, and get the local path of the schema.

UploadSchemaFilePath string
GpgPassphrase string
GpgKeyRing string
AwsRegion string
AwsRoleARN string
// locking properties (candidate for factoring)
AwsLockBucket string
AwsBucket string
AwsTags string
LockGroup string
DisableLock bool
Expand Down Expand Up @@ -85,6 +88,8 @@ func LoadConfig() (Config, error) {
viper.BindEnv("artifacts_src_folder")
viper.BindEnv("aptly_folder")
viper.BindEnv("upload_schema_file_path")
viper.BindEnv("schema_url")
viper.BindEnv("schema")
viper.BindEnv("dest_prefix")
viper.BindEnv("gpg_passphrase")
viper.BindEnv("gpg_key_ring")
Expand Down Expand Up @@ -132,10 +137,13 @@ func LoadConfig() (Config, error) {
ArtifactsSrcFolder: viper.GetString("artifacts_src_folder"),
AptlyFolder: aptlyF,
UploadSchemaFilePath: viper.GetString("upload_schema_file_path"),
SchemaURL: viper.GetString("schema_url"),
Schema: viper.GetString("schema"),
GpgPassphrase: viper.GetString("gpg_passphrase"),
GpgKeyRing: viper.GetString("gpg_key_ring"),
LockGroup: lockGroup,
AwsLockBucket: viper.GetString("aws_s3_lock_bucket_name"),
AwsBucket: viper.GetString("aws_s3_bucket_name"),
AwsRoleARN: viper.GetString("aws_role_arn"),
AwsRegion: viper.GetString("aws_region"),
AwsTags: viper.GetString("aws_tags"),
Expand Down
5 changes: 3 additions & 2 deletions publisher/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/aws/aws-sdk-go v1.37.11
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.10.1
github.com/stretchr/testify v1.7.0
github.com/stretchr/testify v1.10.0
gopkg.in/yaml.v2 v2.4.0
)

Expand All @@ -22,9 +22,10 @@ require (
github.com/spf13/afero v1.6.0 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
golang.org/x/sys v0.0.0-20211210111614-af8b64212486 // indirect
golang.org/x/text v0.3.7 // indirect
gopkg.in/ini.v1 v1.66.2 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
6 changes: 6 additions & 0 deletions publisher/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -291,11 +291,15 @@ github.com/spf13/viper v1.10.1/go.mod h1:IGlFPqhNAPKRxohIzWpI5QEy4kuI7tcl5WvR+8q
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
Expand Down Expand Up @@ -467,6 +471,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
Expand Down
30 changes: 26 additions & 4 deletions publisher/publisher.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ package main

import (
"fmt"
"log"
"net/http"

"github.com/newrelic/infrastructure-publish-action/publisher/config"
"github.com/newrelic/infrastructure-publish-action/publisher/download"
"github.com/newrelic/infrastructure-publish-action/publisher/lock"
"github.com/newrelic/infrastructure-publish-action/publisher/release"
"github.com/newrelic/infrastructure-publish-action/publisher/upload"
"log"
"net/http"
"strings"
)

const (
Expand Down Expand Up @@ -42,6 +43,11 @@ func main() {
l.Fatal("loading config: " + err.Error())
}

releaseMarker, err := newReleaseMarker(conf)
if err != nil {
l.Fatal("creating release marker: " + err.Error())
}

var bucketLock lock.BucketLock
if conf.DisableLock {
bucketLock = lock.NewNoop()
Expand Down Expand Up @@ -105,9 +111,25 @@ func main() {
conf.ArtifactsSrcFolder = conf.LocalPackagesPath
}

err = upload.UploadArtifacts(conf, uploadSchemas, bucketLock)
err = upload.UploadArtifacts(conf, uploadSchemas, bucketLock, releaseMarker)
if err != nil {
l.Fatal(err)
}
l.Println("🎉 upload phase complete")
}

func newReleaseMarker(conf config.Config) (release.Marker, error) {
// We'll leave the release marker file in the root of the repository
// i.e.
// repo = /infrastructure_agent/linux/apt/
// release marker = /infrastructure_agent/releases.json
repoRootDir := strings.Split(strings.TrimPrefix(conf.DestPrefix, "/"), "/")[0]
markerS3Conf := release.S3Config{
Bucket: conf.AwsBucket,
RoleARN: conf.AwsRoleARN,
Region: conf.AwsRegion,
Directory: repoRootDir,
}

return release.NewMarkerAWS(markerS3Conf, l.Printf)
}
59 changes: 59 additions & 0 deletions publisher/release/marker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package release

import (
"encoding/json"
"time"
)

type ReleaseInfo struct {
AppName string `json:"app_name"`
Tag string `json:"tag"`
RunID string `json:"run_id"`
RepoName string `json:"repo_name"`
Schema string `json:"schema"`
SchemaURL string `json:"schema_url"`
}

// Mark represents a release mark. It will contain the name of the release (appName, tag...)
// and the start and end of a release
// When the release has been started, the end will be zero
type Mark struct {
ReleaseInfo
Start CustomTime `json:"start"`
End CustomTime `json:"end"`
}

// Marker abstracts the persistence of the start and end of a release
type Marker interface {
Start(releaseInfo ReleaseInfo) (Mark, error)
End(mark Mark) error
}

// CustomTime is a wrapper around time.Time that
// allows to marshal and unmarshal time.Time in a custom format
type CustomTime struct {
time.Time
}

const ctLayout = time.RFC3339

func (ct *CustomTime) UnmarshalJSON(b []byte) error {
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
t, err := time.Parse(ctLayout, s)
if err != nil {
return err
}
ct.Time = t
return nil
}

func (ct *CustomTime) MarshalJSON() ([]byte, error) {
return json.Marshal(ct.Time.Format(ctLayout))
}

func (ct *CustomTime) Equals(t CustomTime) bool {
return ct.Truncate(time.Second).Equal(t.Truncate(time.Second))
}
Loading
Loading