Skip to content

Commit

Permalink
feat: return error if ec version is a pre-release
Browse files Browse the repository at this point in the history
if the version present in the embedded cluster config is a pre-release,
the linter should return an error.
  • Loading branch information
ricardomaraschini committed Nov 6, 2024
1 parent 472ea24 commit 221f17c
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 25 deletions.
81 changes: 57 additions & 24 deletions pkg/ec/lint.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ec

import (
"encoding/json"
"fmt"
"net/http"
"os"
Expand All @@ -11,11 +12,16 @@ import (
"gopkg.in/yaml.v2"
)

var ecVersions map[string]bool
var ecVersions map[string]EmbeddedClusterVersion
var rwMutex sync.RWMutex
var githubAPIURL = "http://api.github.com"

type EmbeddedClusterVersion struct {
PreRelease bool `json:"prerelease"`
}

func init() {
ecVersions = make(map[string]bool)
ecVersions = make(map[string]EmbeddedClusterVersion)
}

func LintEmbeddedClusterVersion(specFiles domain.SpecFiles) ([]domain.LintExpression, error) {
Expand Down Expand Up @@ -48,7 +54,7 @@ func LintEmbeddedClusterVersion(specFiles domain.SpecFiles) ([]domain.LintExpres
lintExpressions = append(lintExpressions, ecVersionlintExpression)
} else {
// version is defined, check if it is valid.
exists, err := checkIfECVersionExists(version)
ecVersion, exists, err := checkIfECVersionExists(version)
if err != nil {
return nil, errors.Wrap(err, "failed to check if ec version exists")
}
Expand All @@ -60,6 +66,14 @@ func LintEmbeddedClusterVersion(specFiles domain.SpecFiles) ([]domain.LintExpres
Message: "Embedded Cluster version not found",
}
lintExpressions = append(lintExpressions, ecVersionlintExpression)
} else if ecVersion.PreRelease {
ecVersionlintExpression := domain.LintExpression{
Rule: "non-existent-ec-version",
Type: "error",
Path: spec.Path,
Message: "Embedded Cluster version is a pre-release",
}
lintExpressions = append(lintExpressions, ecVersionlintExpression)
}
}
}
Expand All @@ -68,33 +82,52 @@ func LintEmbeddedClusterVersion(specFiles domain.SpecFiles) ([]domain.LintExpres
return lintExpressions, nil
}

func checkIfECVersionExists(version string) (bool, error) {
url := "http://api.github.com/repos/replicatedhq/embedded-cluster/releases/tags/%s"
func checkIfECVersionExists(version string) (*EmbeddedClusterVersion, bool, error) {
url := githubAPIURL + "/repos/replicatedhq/embedded-cluster/releases/tags/%s"
token := os.Getenv("GITHUB_API_TOKEN")
var bearer = "Bearer " + token

rwMutex.RLock()
verIsCached := ecVersions[version]
ecVersion, found := ecVersions[version]
rwMutex.RUnlock()

if !verIsCached {
req, err := http.NewRequest("GET", fmt.Sprintf(url, version), nil)
if err != nil {
return false, errors.Wrap(err, "failed to create new request")
}
req.Header.Set("Authorization", bearer)
client := &http.Client{}
resp, _ := client.Do(req)
if resp.StatusCode == 404 {
return false, nil
} else if resp.StatusCode == 200 {
rwMutex.Lock()
ecVersions[version] = true
rwMutex.Unlock()
} else {
return false, errors.New(fmt.Sprintf("received non 200 status code (%d) from GitHub API request", resp.StatusCode))
}
if found {
return &ecVersion, true, nil
}

req, err := http.NewRequest("GET", fmt.Sprintf(url, version), nil)
if err != nil {
return nil, false, errors.Wrap(err, "failed to create new request")
}
req.Header.Set("Authorization", bearer)

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, false, errors.Wrap(err, "failed to make http request")
}
defer resp.Body.Close()

if resp.StatusCode == http.StatusNotFound {
return nil, false, nil
}

if resp.StatusCode != http.StatusOK {
return nil, false, errors.New(fmt.Sprintf("received non 200 status code (%d) from GitHub API request", resp.StatusCode))
}

var newVersion EmbeddedClusterVersion
if err := json.NewDecoder(resp.Body).Decode(&newVersion); err != nil {
return nil, false, errors.Wrap(err, "failed to decode embedded cluster version json")
}

if newVersion.PreRelease {
return &newVersion, true, nil
}

rwMutex.Lock()
ecVersions[version] = newVersion
rwMutex.Unlock()

return true, nil
return &newVersion, true, nil
}
44 changes: 43 additions & 1 deletion pkg/ec/lint_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package ec

import (
"net/http"
"net/http/httptest"
"testing"

"github.com/replicatedhq/kots-lint/pkg/domain"
Expand All @@ -14,6 +16,7 @@ func Test_LintEmbeddedClusterVersion(t *testing.T) {
name string
specFiles domain.SpecFiles
expect []domain.LintExpression
apiResult []byte
}{
{
name: "valid version",
Expand All @@ -26,7 +29,8 @@ spec:
version: "v1.2.2+k8s-1.29"`,
},
},
expect: []domain.LintExpression{},
expect: []domain.LintExpression{},
apiResult: []byte(`{}`),
},
{
name: "invalid version",
Expand All @@ -47,10 +51,48 @@ spec:
},
},
},
{
name: "pre-release version",
specFiles: domain.SpecFiles{
{
Path: "",
Content: `apiVersion: embeddedcluster.replicated.com/v1beta1
kind: Config
spec:
version: "pre-release-version"`,
},
},
expect: []domain.LintExpression{
{
Rule: "non-existent-ec-version",
Type: "error",
Message: "Embedded Cluster version is a pre-release",
},
},
apiResult: []byte(`{"prerelease": true}`),
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
oldURL := githubAPIURL
defer func() { githubAPIURL = oldURL }()

server := httptest.NewServer(
http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if test.apiResult == nil {
w.WriteHeader(http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(test.apiResult)
},
),
)
defer server.Close()

githubAPIURL = server.URL
actual, err := LintEmbeddedClusterVersion(test.specFiles)
require.NoError(t, err)
assert.ElementsMatch(t, actual, test.expect)
Expand Down

0 comments on commit 221f17c

Please sign in to comment.