-
Notifications
You must be signed in to change notification settings - Fork 21
/
kubernetes.go
163 lines (137 loc) · 5.71 KB
/
kubernetes.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
// Copyright 2022 VMware, Inc. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package discovery
import (
"strings"
"github.com/pkg/errors"
configtypes "github.com/vmware-tanzu/tanzu-plugin-runtime/config/types"
cliv1alpha1 "github.com/vmware-tanzu/tanzu-cli/apis/cli/v1alpha1"
"github.com/vmware-tanzu/tanzu-cli/pkg/cluster"
"github.com/vmware-tanzu/tanzu-cli/pkg/common"
"github.com/vmware-tanzu/tanzu-cli/pkg/distribution"
"github.com/vmware-tanzu/tanzu-cli/pkg/utils"
"github.com/vmware-tanzu/tanzu-plugin-runtime/log"
)
// KubernetesDiscovery is an artifact discovery utilizing CLIPlugin API in kubernetes cluster
type KubernetesDiscovery struct {
name string
kubeconfigPath string
kubecontext string
kubeconfigBytes []byte
}
// NewKubernetesDiscovery returns a new kubernetes repository
func NewKubernetesDiscovery(name, kubeconfigPath, kubecontext string, kubeconfigBytes []byte) Discovery {
return &KubernetesDiscovery{
name: name,
kubeconfigPath: kubeconfigPath,
kubecontext: kubecontext,
kubeconfigBytes: kubeconfigBytes,
}
}
// List available plugins.
func (k *KubernetesDiscovery) List() ([]Discovered, error) {
return k.Manifest()
}
// Name of the repository.
func (k *KubernetesDiscovery) Name() string {
return k.name
}
// Manifest returns the manifest for a kubernetes repository.
func (k *KubernetesDiscovery) Manifest() ([]Discovered, error) {
log.V(6).Infof("creating kubernetes client with kubeconfig %q, kubecontext %q", k.kubeconfigPath, k.kubecontext)
// Create cluster client
clusterClient, err := cluster.NewClient(k.kubeconfigPath, k.kubecontext, k.kubeconfigBytes, cluster.Options{RequestTimeout: defaultTimeout})
if err != nil {
return nil, err
}
return k.GetDiscoveredPlugins(clusterClient)
}
// GetDiscoveredPlugins returns the list of discovered plugin from a kubernetes cluster
func (k *KubernetesDiscovery) GetDiscoveredPlugins(clusterClient cluster.Client) ([]Discovered, error) {
plugins := make([]Discovered, 0)
logMsg := "Skipping context-aware plugin discovery because CLIPlugin CRD not present on the logged in cluster. "
crdExists, errVerifyCRD := clusterClient.VerifyCLIPluginCRD()
// If no error and CRD does not exists, return with a log message
// If there is an error, wait for the call of ListCLIPluginResources to return
// and then decide. As sometimes discovery API can return premature error
// if the k8s apiserver is not stable but ListCLIPluginResources might return
// correct result. In this case, we will ignore the error we get here.
if errVerifyCRD == nil && !crdExists {
log.V(4).Info(logMsg)
return nil, nil
}
// Try to get all cliplugins resources available on the cluster
cliplugins, errListCLIPlugins := clusterClient.ListCLIPluginResources()
if errListCLIPlugins != nil {
// If there was an earlier error while verifying CRD, assuming that it was a legitimate
// error and will just log a warning and return without error
if errVerifyCRD != nil {
logMsg += errVerifyCRD.Error()
log.V(4).Info(logMsg)
return nil, nil
}
// If the CRD was present and we see error while listing the CLIPlugin resources
// log an error message and return an error
log.V(4).Infof("error while fetching the list of CLIPlugin resources. %v", errListCLIPlugins.Error())
return nil, errListCLIPlugins
}
log.V(4).Infof("found %v CLIPlugin resources.", len(cliplugins))
imageRepositoryOverride, err := clusterClient.GetCLIPluginImageRepositoryOverride()
if err != nil {
log.Infof("unable to get image repository override information for some of the plugins. Error: %v", err)
}
// Convert all CLIPlugin resources to Discovered object
for i := range cliplugins {
dp, err := DiscoveredFromK8sV1alpha1WithImageRepositoryOverride(&cliplugins[i], imageRepositoryOverride)
if err != nil {
return nil, err
}
log.V(5).Infof("processing CLIPlugin %q", cliplugins[i].Name)
dp.Source = k.name
dp.DiscoveryType = k.Type()
plugins = append(plugins, dp)
}
return plugins, nil
}
// Type of the repository.
func (k *KubernetesDiscovery) Type() string {
return common.DiscoveryTypeKubernetes
}
// DiscoveredFromK8sV1alpha1WithImageRepositoryOverride returns discovered plugin object from k8sV1alpha1
func DiscoveredFromK8sV1alpha1WithImageRepositoryOverride(p *cliv1alpha1.CLIPlugin, imageRepoOverride map[string]string) (Discovered, error) {
// Update artifacts based on image repository override if applicable
UpdateArtifactsBasedOnImageRepositoryOverride(p, imageRepoOverride)
dp := Discovered{
Name: p.Name,
Description: p.Spec.Description,
RecommendedVersion: p.Spec.RecommendedVersion,
Optional: p.Spec.Optional,
Target: configtypes.StringToTarget(string(p.Spec.Target)),
}
dp.SupportedVersions = make([]string, 0)
for v := range p.Spec.Artifacts {
dp.SupportedVersions = append(dp.SupportedVersions, v)
}
if err := utils.SortVersions(dp.SupportedVersions); err != nil {
return dp, errors.Wrapf(err, "error parsing supported versions for plugin %s", p.Name)
}
dp.Distribution = distribution.ArtifactsFromK8sV1alpha1(p.Spec.Artifacts)
return dp, nil
}
// UpdateArtifactsBasedOnImageRepositoryOverride updates artifacts based on image repository override
func UpdateArtifactsBasedOnImageRepositoryOverride(p *cliv1alpha1.CLIPlugin, imageRepoOverride map[string]string) {
replaceImageRepository := func(a *cliv1alpha1.Artifact) {
if a.Image != "" {
for originalRepo, overrideRepo := range imageRepoOverride {
if strings.HasPrefix(a.Image, originalRepo) {
a.Image = strings.Replace(a.Image, originalRepo, overrideRepo, 1)
}
}
}
}
for i := range p.Spec.Artifacts {
for j := range p.Spec.Artifacts[i] {
replaceImageRepository(&p.Spec.Artifacts[i][j])
}
}
}