Files
kubesphere/pkg/models/openpitrix/utils.go
LiHui 83e6221f3a openpitrix crd
Signed-off-by: LiHui <andrewli@yunify.com>

delete helm repo, release and app

Signed-off-by: LiHui <andrewli@yunify.com>

Fix Dockerfile

Signed-off-by: LiHui <andrewli@yunify.com>

add unit test for category controller

Signed-off-by: LiHui <andrewli@yunify.com>

resource api

Signed-off-by: LiHui <andrewli@yunify.com>

miscellaneous

Signed-off-by: LiHui <andrewli@yunify.com>

resource api

Signed-off-by: LiHui <andrewli@yunify.com>

add s3 repo indx

Signed-off-by: LiHui <andrewli@yunify.com>

attachment api

Signed-off-by: LiHui <andrewli@yunify.com>

repo controller test

Signed-off-by: LiHui <andrewli@yunify.com>

application controller test

Signed-off-by: LiHui <andrewli@yunify.com>

release metric

Signed-off-by: LiHui <andrewli@yunify.com>

helm release controller test

Signed-off-by: LiHui <andrewli@yunify.com>

move constants to /pkg/apis/application

Signed-off-by: LiHui <andrewli@yunify.com>

remove unused code

Signed-off-by: LiHui <andrewli@yunify.com>

add license header

Signed-off-by: LiHui <andrewli@yunify.com>

Fix bugs

Signed-off-by: LiHui <andrewli@yunify.com>

cluster cluent

Signed-off-by: LiHui <andrewli@yunify.com>

format code

Signed-off-by: LiHui <andrewli@yunify.com>

move workspace,cluster from spec to labels

Signed-off-by: LiHui <andrewli@yunify.com>

add license header

Signed-off-by: LiHui <andrewli@yunify.com>

openpitrix test

Signed-off-by: LiHui <andrewli@yunify.com>

add worksapce labels for app in appstore

Signed-off-by: LiHui <andrewli@yunify.com>
2021-03-09 18:30:02 +08:00

693 lines
17 KiB
Go

/*
Copyright 2020 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package openpitrix
import (
"fmt"
"github.com/Masterminds/semver/v3"
"github.com/go-openapi/strfmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/cli-runtime/pkg/resource"
"kubesphere.io/kubesphere/pkg/apis/application/v1alpha1"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/server/params"
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix/helmrepoindex"
"kubesphere.io/kubesphere/pkg/utils/idutils"
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
"kubesphere.io/kubesphere/pkg/utils/stringutils"
"path"
"regexp"
"sort"
"strings"
"time"
)
func convertRepoEvent(meta *metav1.ObjectMeta, state *v1alpha1.HelmRepoSyncState) *RepoEvent {
if meta == nil || state == nil {
return nil
}
out := RepoEvent{}
date := strfmt.DateTime(time.Unix(state.SyncTime.Unix(), 0))
out.CreateTime = &date
out.RepoId = meta.Name
out.RepoEventId = ""
out.Result = state.Message
out.Status = state.State
out.StatusTime = out.CreateTime
return &out
}
func convertAppVersionAudit(appVersion *v1alpha1.HelmApplicationVersion) []*AppVersionAudit {
if appVersion == nil {
return nil
}
var audits []*AppVersionAudit
for _, helmAudit := range appVersion.Status.Audit {
var audit AppVersionAudit
audit.AppId = appVersion.GetHelmApplicationId()
audit.Operator = helmAudit.Operator
audit.Message = helmAudit.Message
audit.Status = helmAudit.State
date := strfmt.DateTime(time.Unix(helmAudit.Time.Unix(), 0))
audit.StatusTime = &date
audit.VersionId = appVersion.Name
audit.VersionType = "helm"
audit.VersionName = appVersion.GetVersionName()
audit.Operator = helmAudit.Operator
audit.OperatorType = helmAudit.OperatorType
audits = append(audits, &audit)
}
return audits
}
type HelmReleaseList []*v1alpha1.HelmRelease
func (l HelmReleaseList) Len() int { return len(l) }
func (l HelmReleaseList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l HelmReleaseList) Less(i, j int) bool {
var t1, t2 time.Time
if l[i].Status.LastUpdate.IsZero() {
t1 = l[i].CreationTimestamp.Time
} else {
t1 = l[i].Status.LastUpdate.Time
}
if l[j].Status.LastUpdate.IsZero() {
t2 = l[j].CreationTimestamp.Time
} else {
t2 = l[j].Status.LastUpdate.Time
}
if t1.After(t2) {
return true
} else if t1.Before(t2) {
return false
} else {
return l[i].Name > l[j].Name
}
}
type AppVersionAuditList []*AppVersionAudit
func (l AppVersionAuditList) Len() int { return len(l) }
func (l AppVersionAuditList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l AppVersionAuditList) Less(i, j int) bool {
t1 := l[i].StatusTime.String()
t2 := l[j].StatusTime.String()
if t1 > t2 {
return true
} else if t1 < t2 {
return false
} else {
n1 := l[i].VersionName
n2 := l[j].VersionName
return n1 < n2
}
}
// copy from openpitrix
func matchPackageFailedError(err error, res *ValidatePackageResponse) {
var errStr = err.Error()
var matchedError = ""
var errorDetails = make(map[string]string)
switch {
// Helm errors
case strings.HasPrefix(errStr, "no files in chart archive"),
strings.HasPrefix(errStr, "no files in app archive"):
matchedError = "no files in package"
case strings.HasPrefix(errStr, "chart yaml not in base directory"),
strings.HasPrefix(errStr, "chart metadata (Chart.yaml) missing"):
errorDetails["Chart.yaml"] = "not found"
case strings.HasPrefix(errStr, "invalid chart (Chart.yaml): name must not be empty"):
errorDetails["Chart.yaml"] = "package name must not be empty"
case strings.HasPrefix(errStr, "values.toml is illegal"):
errorDetails["values.toml"] = errStr
case strings.HasPrefix(errStr, "error reading"):
matched := regexp.MustCompile("error reading (.+): (.+)").FindStringSubmatch(errStr)
if len(matched) > 0 {
errorDetails[matched[1]] = matched[2]
}
// Devkit errors
case strings.HasPrefix(errStr, "[package.json] not in base directory"):
errorDetails["package.json"] = "not found"
case strings.HasPrefix(errStr, "missing file ["):
matched := regexp.MustCompile("missing file \\[(.+)]").FindStringSubmatch(errStr)
if len(matched) > 0 {
errorDetails[matched[1]] = "not found"
}
case strings.HasPrefix(errStr, "failed to parse"),
strings.HasPrefix(errStr, "failed to render"),
strings.HasPrefix(errStr, "failed to load"),
strings.HasPrefix(errStr, "failed to decode"):
matched := regexp.MustCompile("failed to (.+) (.+): (.+)").FindStringSubmatch(errStr)
if len(matched) > 0 {
errorDetails[matched[2]] = fmt.Sprintf("%s failed, %s", matched[1], matched[3])
}
default:
matchedError = errStr
}
if len(errorDetails) > 0 {
res.ErrorDetails = errorDetails
}
if len(matchedError) > 0 {
res.Error = matchedError
}
}
func convertCategory(in *v1alpha1.HelmCategory) *Category {
if in == nil {
return nil
}
out := &Category{}
out.Description = in.Spec.Description
out.Name = in.Spec.Name
out.CategoryID = in.Name
t := strfmt.DateTime(in.CreationTimestamp.Time)
out.CreateTime = &t
if in.Spec.Locale == "" {
out.Locale = "{}"
} else {
out.Locale = in.Spec.Locale
}
total := in.Status.Total
out.AppTotal = &total
return out
}
func convertApplication(rls *v1alpha1.HelmRelease, rlsInfos []*resource.Info) *Application {
app := &Application{}
app.Name = rls.Spec.ChartName
cluster := &Cluster{}
cluster.ClusterId = rls.Name
cluster.Owner = rls.GetCreator()
cluster.Zone = rls.GetRlsNamespace()
cluster.Status = rls.Status.State
cluster.Env = string(rls.Spec.Values)
if cluster.Status == "" {
cluster.Status = v1alpha1.HelmStatusPending
}
cluster.AdditionalInfo = rls.Status.Message
cluster.Description = rls.Spec.Description
dt := strfmt.DateTime(rls.CreationTimestamp.Time)
cluster.CreateTime = &dt
if !rls.Status.LastUpdate.Time.IsZero() {
ut := strfmt.DateTime(rls.Status.LastUpdate.Time)
cluster.StatusTime = &ut
} else {
cluster.StatusTime = &dt
}
cluster.AppId = rls.Spec.ApplicationId
cluster.VersionId = rls.Spec.ApplicationVersionId
cluster.Name = rls.GetTrueName()
if rls.GetRlsCluster() != "" {
cluster.RuntimeId = rls.GetRlsCluster()
} else {
cluster.RuntimeId = "default"
}
app.Cluster = cluster
app.Version = &AppVersion{
AppId: rls.Spec.ApplicationId,
VersionId: rls.Spec.ApplicationVersionId,
Name: rls.GetChartVersionName(),
}
app.App = &App{
AppId: rls.Spec.ApplicationId,
ChartName: rls.Spec.ChartName,
Name: rls.Spec.ChartName,
}
app.ReleaseInfo = make([]runtime.Object, 0, len(rlsInfos))
if rlsInfos != nil {
for _, info := range rlsInfos {
app.ReleaseInfo = append(app.ReleaseInfo, info.Object)
}
}
return app
}
func convertApp(app *v1alpha1.HelmApplication, versions []*v1alpha1.HelmApplicationVersion, ctg *v1alpha1.HelmCategory, rlsCount int) *App {
if app == nil {
return nil
}
out := &App{}
out.AppId = app.Name
out.Name = app.GetTrueName()
date := strfmt.DateTime(app.CreationTimestamp.Time)
out.CreateTime = &date
if app.Status.StatusTime != nil {
s := strfmt.DateTime(app.Status.StatusTime.Time)
out.StatusTime = &s
} else {
out.StatusTime = out.CreateTime
}
if app.Status.UpdateTime == nil {
out.UpdateTime = out.CreateTime
} else {
u := strfmt.DateTime(app.Status.UpdateTime.Time)
out.UpdateTime = &u
}
out.Status = app.Status.State
if out.Status == "" {
out.Status = v1alpha1.StateDraft
}
out.Abstraction = app.Spec.Abstraction
out.Description = app.Spec.Description
if len(app.Spec.Attachments) > 0 {
out.Screenshots = strings.Join(app.Spec.Attachments, ",")
}
out.Home = app.Spec.AppHome
out.Icon = app.Spec.Icon
if ctg != nil {
ct := strfmt.DateTime(ctg.CreationTimestamp.Time)
rc := ResourceCategory{
CategoryId: ctg.Name,
Name: ctg.GetTrueName(),
CreateTime: &ct,
Locale: ctg.Spec.Locale,
}
if ctg.Spec.Locale == "" {
rc.Locale = "{}"
} else {
rc.Locale = ctg.Spec.Locale
}
rc.Status = "enabled"
out.CategorySet = AppCategorySet{&rc}
} else {
out.CategorySet = AppCategorySet{}
}
if versions != nil && len(versions) > 0 {
sort.Sort(AppVersions(versions))
out.LatestAppVersion = convertAppVersion(versions[0])
} else {
out.LatestAppVersion = &AppVersion{}
}
out.AppVersionTypes = "helm"
out.Isv = app.GetWorkspace()
out.ClusterTotal = &rlsCount
return out
}
func filterAppVersionByState(versions []*v1alpha1.HelmApplicationVersion, states []string) []*v1alpha1.HelmApplicationVersion {
if len(states) == 0 {
return versions
}
var j = 0
for i := 0; i < len(versions); i++ {
state := versions[i].State()
if sliceutil.HasString(states, state) {
if i != j {
versions[j] = versions[i]
}
j++
}
}
versions = versions[:j:j]
return versions
}
func convertAppVersion(in *v1alpha1.HelmApplicationVersion) *AppVersion {
if in == nil {
return nil
}
out := AppVersion{}
out.AppId = in.GetHelmApplicationId()
out.Active = true
t := in.CreationTimestamp.Time
date := strfmt.DateTime(t)
out.CreateTime = &date
if len(in.Status.Audit) > 0 {
t = in.Status.Audit[0].Time.Time
updateDate := strfmt.DateTime(t)
out.UpdateTime = &updateDate
} else {
out.UpdateTime = &date
}
if in.Spec.Metadata != nil {
out.Description = in.Spec.Description
out.Icon = in.Spec.Icon
}
out.Status = in.State()
out.Owner = in.GetCreator()
out.Name = in.GetVersionName()
out.VersionId = in.GetHelmApplicationVersionId()
return &out
}
func convertRepo(in *v1alpha1.HelmRepo) *Repo {
if in == nil {
return nil
}
out := Repo{}
out.RepoId = in.GetHelmRepoId()
out.Name = in.GetTrueName()
out.Status = "active"
date := strfmt.DateTime(time.Unix(in.CreationTimestamp.Unix(), 0))
out.CreateTime = &date
out.Description = in.Spec.Description
out.URL = in.Spec.Url
return &out
}
type HelmCategoryList []*v1alpha1.HelmCategory
func (l HelmCategoryList) Len() int { return len(l) }
func (l HelmCategoryList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l HelmCategoryList) Less(i, j int) bool {
t1 := l[i].CreationTimestamp.UnixNano()
t2 := l[j].CreationTimestamp.UnixNano()
if t1 > t2 {
return true
} else if t1 < t2 {
return false
} else {
n1 := l[i].Spec.Name
n2 := l[j].Spec.Name
return n1 < n2
}
}
type HelmApplicationList []*v1alpha1.HelmApplication
func (l HelmApplicationList) Len() int { return len(l) }
func (l HelmApplicationList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l HelmApplicationList) Less(i, j int) bool {
t1 := l[i].CreationTimestamp.UnixNano()
t2 := l[j].CreationTimestamp.UnixNano()
if t1 < t2 {
return true
} else if t1 > t2 {
return false
} else {
n1 := l[i].GetTrueName()
n2 := l[j].GetTrueName()
return n1 < n2
}
}
type AppVersions []*v1alpha1.HelmApplicationVersion
// Len returns the length.
func (c AppVersions) Len() int { return len(c) }
// Swap swaps the position of two items in the versions slice.
func (c AppVersions) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
// Less returns true if the version of entry a is less than the version of entry b.
func (c AppVersions) Less(a, b int) bool {
// Failed parse pushes to the back.
aVersion := c[a]
bVersion := c[b]
i, err := semver.NewVersion(aVersion.GetSemver())
if err != nil {
return true
}
j, err := semver.NewVersion(bVersion.GetSemver())
if err != nil {
return false
}
if i.Equal(j) {
return aVersion.CreationTimestamp.Before(&bVersion.CreationTimestamp)
}
return j.LessThan(i)
}
// buildApplicationVersion build an application version
// packageData base64 encoded package data
func buildApplicationVersion(app *v1alpha1.HelmApplication, chrt helmrepoindex.VersionInterface, chartPackage *string, creator string) *v1alpha1.HelmApplicationVersion {
// create helm application version resource
t := metav1.Now()
ver := &v1alpha1.HelmApplicationVersion{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
constants.CreatorAnnotationKey: creator,
},
Name: idutils.GetUuid36(v1alpha1.HelmApplicationVersionIdPrefix),
Labels: map[string]string{
constants.ChartApplicationIdLabelKey: app.GetHelmApplicationId(),
constants.WorkspaceLabelKey: app.GetWorkspace(),
},
OwnerReferences: []metav1.OwnerReference{
{
UID: app.GetUID(),
APIVersion: v1alpha1.SchemeGroupVersion.String(),
Kind: "HelmApplication",
Name: app.Name,
},
},
},
Spec: v1alpha1.HelmApplicationVersionSpec{
Metadata: &v1alpha1.Metadata{
Version: chrt.GetVersion(),
AppVersion: chrt.GetAppVersion(),
Name: chrt.GetName(),
Icon: chrt.GetIcon(),
Home: chrt.GetHome(),
Description: stringutils.ShortenString(chrt.GetDescription(), v1alpha1.MsgLen),
},
Created: &t,
// set data to nil before save app version to etcd
Data: []byte(*chartPackage),
},
Status: v1alpha1.HelmApplicationVersionStatus{
State: v1alpha1.StateDraft,
Audit: []v1alpha1.Audit{
{
State: v1alpha1.StateDraft,
Time: t,
Operator: creator,
},
},
},
}
return ver
}
func filterAppByName(app *v1alpha1.HelmApplication, namePart string) bool {
if len(namePart) == 0 {
return true
}
name := app.GetTrueName()
if strings.HasSuffix(name, namePart) || strings.HasPrefix(name, namePart) {
return true
}
return false
}
func filterAppByStates(app *v1alpha1.HelmApplication, state []string) bool {
if len(state) == 0 {
return true
}
st := app.Status.State
// default value is draft
if st == "" {
st = v1alpha1.StateDraft
}
if sliceutil.HasString(state, st) {
return true
}
return false
}
func filterApps(apps []*v1alpha1.HelmApplication, conditions *params.Conditions) []*v1alpha1.HelmApplication {
if conditions == nil || len(conditions.Match) == 0 || len(apps) == 0 {
return apps
}
curr := 0
for i := 0; i < len(apps); i++ {
if conditions.Match[Keyword] != "" {
fv := filterAppByName(apps[i], conditions.Match[Keyword])
if !fv {
continue
}
}
if conditions.Match[Status] != "" {
states := strings.Split(conditions.Match[Status], "|")
fv := filterAppByStates(apps[i], states)
if !fv {
continue
}
}
if curr != i {
apps[curr] = apps[i]
}
curr++
}
return apps[:curr:curr]
}
func filterReleaseByName(rls *v1alpha1.HelmRelease, namePart string) bool {
if len(namePart) == 0 {
return true
}
name := rls.GetTrueName()
if strings.HasSuffix(name, namePart) || strings.HasPrefix(name, namePart) {
return true
}
return false
}
func filterReleaseByStates(rls *v1alpha1.HelmRelease, state []string) bool {
if len(state) == 0 {
return true
}
st := rls.Status.State
if st == "" {
st = v1alpha1.HelmStatusCreating
}
if sliceutil.HasString(state, st) {
return true
}
return false
}
func filterReleasesWithAppVersions(releases []*v1alpha1.HelmRelease, appVersions map[string]*v1alpha1.HelmApplicationVersion) []*v1alpha1.HelmRelease {
if appVersions == nil || len(appVersions) == 0 || len(releases) == 0 {
return []*v1alpha1.HelmRelease{}
}
curr := 0
for i := 0; i < len(releases); i++ {
if _, exists := appVersions[releases[i].Spec.ApplicationVersionId]; exists {
if curr != i {
releases[curr] = releases[i]
}
curr++
}
}
return releases[:curr:curr]
}
func filterReleases(releases []*v1alpha1.HelmRelease, conditions *params.Conditions) []*v1alpha1.HelmRelease {
if conditions == nil || len(conditions.Match) == 0 || len(releases) == 0 {
return releases
}
curr := 0
for i := 0; i < len(releases); i++ {
if conditions.Match[Keyword] != "" {
fv := filterReleaseByName(releases[i], conditions.Match[Keyword])
if !fv {
continue
}
}
if conditions.Match[Status] != "" {
states := strings.Split(conditions.Match[Status], "|")
fv := filterReleaseByStates(releases[i], states)
if !fv {
continue
}
}
if curr != i {
releases[curr] = releases[i]
}
curr++
}
return releases[:curr:curr]
}
func dataKeyInStorage(workspace, id string) string {
return path.Join(workspace, id)
}
func attachmentKeyInStorage(ws, id string) string {
return path.Join(ws, id)
}
func convertAppVersionReview(appVersion *v1alpha1.HelmApplicationVersion) *AppVersionReview {
review := &AppVersionReview{}
status := appVersion.Status
review.Reviewer = status.Audit[0].Operator
review.ReviewId = status.Audit[0].Operator
review.Status = appVersion.Status.State
review.AppId = appVersion.GetHelmApplicationId()
review.VersionID = appVersion.GetHelmApplicationVersionId()
review.Phase = AppVersionReviewPhaseOAIGen{}
review.VersionName = appVersion.GetVersionName()
review.StatusTime = strfmt.DateTime(status.Audit[0].Time.Time)
review.AppName = appVersion.GetTrueName()
return review
}
func parseChartVersionName(name string) (version, appVersion string) {
name = strings.TrimSpace(name)
if name == "" {
return "", ""
}
parts := strings.Split(name, "[")
if len(parts) == 1 {
return parts[0], ""
}
version = strings.TrimSpace(parts[0])
appVersion = strings.Trim(parts[1], "]")
appVersion = strings.TrimSpace(appVersion)
return
}