-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 3017951
Showing
11 changed files
with
1,626 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
module github.com/fairwindsops/nova | ||
|
||
go 1.13 | ||
|
||
require ( | ||
github.com/DATA-DOG/go-sqlmock v1.4.1 // indirect | ||
github.com/ashwanthkumar/slack-go-webhook v0.0.0-20200209025033-430dd4e66960 | ||
github.com/bradleyfalzon/ghinstallation v1.1.1 | ||
github.com/google/go-github/v28 v28.1.1 | ||
github.com/jmoiron/sqlx v1.2.0 // indirect | ||
github.com/lib/pq v1.5.2 // indirect | ||
github.com/mcuadros/go-version v0.0.0-20190830083331-035f6764e8d2 | ||
github.com/parnurzeal/gorequest v0.2.16 // indirect | ||
github.com/rubenv/sql-migrate v0.0.0-20200429072036-ae26b214fa43 // indirect | ||
github.com/spf13/cobra v1.0.0 | ||
github.com/spf13/pflag v1.0.5 | ||
github.com/stretchr/testify v1.5.1 | ||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 | ||
gopkg.in/yaml.v2 v2.2.8 | ||
helm.sh/helm v2.16.6+incompatible | ||
helm.sh/helm/v3 v3.1.2 | ||
k8s.io/api v0.18.1 // indirect | ||
k8s.io/apiextensions-apiserver v0.18.1 // indirect | ||
k8s.io/apimachinery v0.18.1 // indirect | ||
k8s.io/client-go v1.5.1 | ||
k8s.io/helm v2.16.5+incompatible | ||
k8s.io/klog v1.0.0 | ||
moul.io/http2curl v1.0.0 // indirect | ||
sigs.k8s.io/controller-runtime v0.5.2 | ||
) | ||
|
||
replace ( | ||
// github.com/Azure/go-autorest/autorest has different versions for the Go | ||
// modules than it does for releases on the repository. Note the correct | ||
// version when updating. | ||
github.com/Azure/go-autorest/autorest => github.com/Azure/go-autorest/autorest v0.9.0 | ||
github.com/docker/docker => github.com/moby/moby v0.7.3-0.20190826074503-38ab9da00309 | ||
|
||
// Kubernetes imports github.com/miekg/dns at a newer version but it is used | ||
// by a package Helm does not need. Go modules resolves all packages rather | ||
// than just those in use (like Glide and dep do). This sets the version | ||
// to the one oras needs. If oras is updated the version should be updated | ||
// as well. | ||
github.com/miekg/dns => github.com/miekg/dns v0.0.0-20181005163659-0d29b283ac0f | ||
gopkg.in/inf.v0 v0.9.1 => github.com/go-inf/inf v0.9.1 | ||
gopkg.in/square/go-jose.v2 v2.3.0 => github.com/square/go-jose v2.3.0+incompatible | ||
k8s.io/api => k8s.io/api v0.0.0-20191016110408-35e52d86657a | ||
k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.0.0-20191114105449-027877536833 | ||
k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20191028221656-72ed19daf4bb | ||
k8s.io/apiserver => k8s.io/apiserver v0.0.0-20191114103151-9ca1dc586682 | ||
k8s.io/cli-runtime => k8s.io/cli-runtime v0.0.0-20191114110141-0a35778df828 | ||
k8s.io/client-go => k8s.io/client-go v0.0.0-20191016111102-bec269661e48 | ||
k8s.io/cloud-provider => k8s.io/cloud-provider v0.0.0-20191114112024-4bbba8331835 | ||
k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.0.0-20191114111741-81bb9acf592d | ||
k8s.io/code-generator => k8s.io/code-generator v0.0.0-20191004115455-8e001e5d1894 | ||
k8s.io/component-base => k8s.io/component-base v0.0.0-20191114102325-35a9586014f7 | ||
k8s.io/cri-api => k8s.io/cri-api v0.0.0-20190828162817-608eb1dad4ac | ||
k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.0.0-20191114112310-0da609c4ca2d | ||
k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.0.0-20191114103820-f023614fb9ea | ||
k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.0.0-20191114111510-6d1ed697a64b | ||
k8s.io/kube-proxy => k8s.io/kube-proxy v0.0.0-20191114110717-50a77e50d7d9 | ||
k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.0.0-20191114111229-2e90afcb56c7 | ||
k8s.io/kubectl => k8s.io/kubectl v0.0.0-20191016120415-2ed914427d51 | ||
k8s.io/kubelet => k8s.io/kubelet v0.0.0-20191114110954-d67a8e7e2200 | ||
k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.0.0-20191114112655-db9be3e678bb | ||
k8s.io/metrics => k8s.io/metrics v0.0.0-20191114105837-a4a2842dc51b | ||
k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.0.0-20191114104439-68caf20693ac | ||
|
||
rsc.io/letsencrypt => github.com/dmcgowan/letsencrypt v0.0.0-20160928181947-1847a81d2087 | ||
) |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,326 @@ | ||
package helm | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"io/ioutil" | ||
"net/http" | ||
"strings" | ||
"sync" | ||
"time" | ||
|
||
version "github.com/mcuadros/go-version" | ||
"helm.sh/helm/v3/pkg/chart" | ||
"helm.sh/helm/v3/pkg/release" | ||
|
||
"gopkg.in/yaml.v2" | ||
chartV2 "k8s.io/helm/pkg/proto/hapi/chart" | ||
rspb "k8s.io/helm/pkg/proto/hapi/release" | ||
"k8s.io/klog" | ||
) | ||
|
||
// Repo represents a Helm chart Repo | ||
type Repo struct { | ||
URL string | ||
Charts *ChartReleases | ||
} | ||
|
||
// ChartReleases contains the chart releases of a helm repository | ||
type ChartReleases struct { | ||
APIVersion string `yaml:"apiVersion"` | ||
Entries map[string][]ChartRelease `yaml:"entries"` | ||
} | ||
|
||
// ChartRelease is a single chart version in a helm repository | ||
type ChartRelease struct { | ||
APIVersion string `yaml:"apiVersion,omitempty"` | ||
AppVersion string `yaml:"appVersion"` | ||
Created time.Time `yaml:"created"` | ||
Description string `yaml:"description"` | ||
Digest string `yaml:"digest,omitempty"` | ||
Maintainers []chart.Maintainer `yaml:"maintainers,omitempty"` | ||
Name string `yaml:"name"` | ||
Urls []string `yaml:"urls"` | ||
Version string `yaml:"version"` | ||
Home string `json:"home"` | ||
Sources []string `json:"sources"` | ||
Keywords []string `json:"keywords"` | ||
Icon string `json:"icon"` | ||
Deprecated bool `json:"deprecated"` | ||
} | ||
|
||
// NewRepo returns data about a helm chart repository, given its url | ||
func NewRepo(urls []string) []*Repo { | ||
var repos []*Repo | ||
|
||
var mutex = &sync.Mutex{} | ||
var wg sync.WaitGroup | ||
wg.Add(len(urls)) | ||
|
||
for _, url := range urls { | ||
go func(address string) { | ||
defer wg.Done() | ||
repo := &Repo{ | ||
URL: address, | ||
Charts: &ChartReleases{}, | ||
} | ||
err := repo.loadReleases() | ||
if err != nil { | ||
klog.Warningf("Could not load chart repo: %s", address) | ||
} else { | ||
mutex.Lock() | ||
repos = append(repos, repo) | ||
mutex.Unlock() | ||
} | ||
}(url) | ||
} | ||
|
||
wg.Wait() | ||
return repos | ||
} | ||
|
||
func (r *Repo) loadReleases() error { | ||
response, err := http.Get(fmt.Sprintf("%s/index.yaml", r.URL)) | ||
if err != nil { | ||
klog.Warningf("Error loading chart repo %s: %v", r.URL, err) | ||
return err | ||
} | ||
|
||
data, err := ioutil.ReadAll(response.Body) | ||
if err != nil { | ||
klog.Warningf("Error reading chart data from repo %s: %v", r.URL, err) | ||
return err | ||
} | ||
|
||
err = yaml.Unmarshal(data, r.Charts) | ||
if err != nil { | ||
klog.Warningf("Error unmarshalling yaml for chart repo %s: %v", r.URL, err) | ||
return err | ||
} | ||
return nil | ||
} | ||
|
||
// NewestVersion returns the newest chart release for the provided release name | ||
func (r *Repo) NewestVersion(releaseName string) *ChartRelease { | ||
for name, entries := range r.Charts.Entries { | ||
if name == releaseName { | ||
var newest ChartRelease | ||
for _, release := range entries { | ||
if IsValidRelease(release.Version) { | ||
if newest.Version == "" { | ||
newest = release | ||
} | ||
|
||
foundNewer := version.Compare(release.Version, newest.Version, ">") | ||
if foundNewer { | ||
newest = release | ||
} | ||
} | ||
} | ||
return &newest | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
// NewestChartVersion returns the newest chart release for the provided release name and version | ||
func (r *Repo) NewestChartVersion(currentChart *chart.Metadata) *ChartRelease { | ||
for name, entries := range r.Charts.Entries { | ||
if name == currentChart.Name { | ||
var newest ChartRelease | ||
repoHasCurrentVersion := false | ||
for _, release := range entries { | ||
if IsValidRelease(release.Version) { | ||
if release.Version == currentChart.Version { | ||
repoHasCurrentVersion = checkChartsSimilarity(currentChart, &release) | ||
} | ||
|
||
foundNewer := version.Compare(release.Version, newest.Version, ">") | ||
if foundNewer { | ||
newest = release | ||
} | ||
} | ||
} | ||
if repoHasCurrentVersion { | ||
return &newest | ||
} | ||
|
||
} | ||
} | ||
return nil | ||
} | ||
|
||
// TryToFindNewestReleaseByChart will return the newest chart release given a collection of repos | ||
func TryToFindNewestReleaseByChart(chart *release.Release, repos []*Repo) *ChartRelease { | ||
newestRelease := &ChartRelease{} | ||
for _, repo := range repos { | ||
newestInRepo := repo.NewestChartVersion(chart.Chart.Metadata) | ||
if newestInRepo == nil { | ||
continue | ||
} | ||
if newestRelease == nil { | ||
newestRelease = newestInRepo | ||
} else { | ||
if version.Compare(newestInRepo.Version, newestRelease.Version, ">") { | ||
newestRelease = newestInRepo | ||
} | ||
} | ||
} | ||
return newestRelease | ||
} | ||
|
||
// TryToFindNewestReleaseByChartVersion2 will return the newest chart release given a collection of repos from helm2 | ||
func TryToFindNewestReleaseByChartVersion2(chart *rspb.Release, repos []*Repo) *ChartRelease { | ||
newestRelease := &ChartRelease{} | ||
for _, repo := range repos { | ||
metadata, err := convertMetadataVersion2to3(chart.Chart.Metadata) | ||
|
||
if err != nil { | ||
klog.Errorf("Error converting helm2 metadata to helm3: %v", err) | ||
continue | ||
} | ||
|
||
newestInRepo := repo.NewestChartVersion(metadata) | ||
if newestInRepo == nil { | ||
continue | ||
} | ||
if newestRelease == nil { | ||
newestRelease = newestInRepo | ||
} else { | ||
if version.Compare(newestInRepo.Version, newestRelease.Version, ">") { | ||
newestRelease = newestInRepo | ||
} | ||
} | ||
} | ||
return newestRelease | ||
} | ||
|
||
func checkChartsSimilarity(currentChartMeta *chart.Metadata, chartFromRepo *ChartRelease) bool { | ||
|
||
if currentChartMeta.Home != chartFromRepo.Home { | ||
return false | ||
} | ||
|
||
if currentChartMeta.Description != chartFromRepo.Description { | ||
return false | ||
} | ||
|
||
for _, source := range currentChartMeta.Sources { | ||
if !containsString(chartFromRepo.Sources, source) { | ||
return false | ||
} | ||
} | ||
|
||
chartFromRepoMaintainers := map[string]bool{} | ||
for _, m := range chartFromRepo.Maintainers { | ||
chartFromRepoMaintainers[m.Email+";"+m.Name+";"+m.URL] = true | ||
} | ||
for _, m := range currentChartMeta.Maintainers { | ||
if !chartFromRepoMaintainers[m.Email+";"+m.Name+";"+m.URL] { | ||
return false | ||
} | ||
} | ||
return true | ||
} | ||
|
||
func containsString(arr []string, val string) bool { | ||
for _, item := range arr { | ||
if item == val { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
// GetNewestReleaseByName will return the newest chart release given a collection of repos | ||
func GetNewestReleaseByName(name string, repos []*Repo) *ChartRelease { | ||
newestRelease := &ChartRelease{} | ||
for _, repo := range repos { | ||
newestInRepo := repo.NewestVersion(name) | ||
if newestRelease == nil { | ||
newestRelease = newestInRepo | ||
} else { | ||
if version.Compare(newestInRepo.Version, newestRelease.Version, ">") { | ||
newestRelease = newestInRepo | ||
} | ||
} | ||
} | ||
return newestRelease | ||
} | ||
|
||
// GetChartInfo returns info about a chart with the version specified | ||
func GetChartInfo(name string, version string, repos []*Repo) *ChartRelease { | ||
for _, repo := range repos { | ||
for key, chart := range repo.Charts.Entries { | ||
if key == name { | ||
for _, release := range chart { | ||
if release.Version == version { | ||
return &release | ||
} | ||
} | ||
} | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
// IsValidRelease returns a bool indicating whether a version string is valid or not. | ||
func IsValidRelease(version string) bool { | ||
var specialForms = []string{ | ||
"SNAPSHOT", | ||
"snapshot", | ||
"dev", | ||
"alpha", | ||
"a", | ||
"beta", | ||
"b", | ||
"RC", | ||
"rc", | ||
"#", | ||
"p", | ||
"pl", | ||
} | ||
|
||
for _, invalid := range specialForms { | ||
if strings.Contains(version, invalid) { | ||
return false | ||
} | ||
} | ||
return true | ||
} | ||
|
||
// IsRepoIncluded check if the repo is included in the list of repos | ||
func IsRepoIncluded(chartName string, repos []*Repo) []*Repo { | ||
found := []*Repo{} | ||
for _, repo := range repos { | ||
if contains(chartName, repo) { | ||
found = append(found, repo) | ||
} | ||
} | ||
return found | ||
} | ||
|
||
func contains(chartName string, repo *Repo) bool { | ||
for name := range repo.Charts.Entries { | ||
if name == chartName { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
func convertMetadataVersion2to3(metatataV2 *chartV2.Metadata) (*chart.Metadata, error) { | ||
|
||
metadata := new(chart.Metadata) | ||
jsonData, err := json.Marshal(metatataV2) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
err = json.Unmarshal(jsonData, metadata) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return metadata, err | ||
} |
Oops, something went wrong.