github.com/verrazzano/verrazzano@v1.7.0/tools/vz/pkg/helpers/vzhelper.go (about) 1 // Copyright (c) 2022, 2023, Oracle and/or its affiliates. 2 // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 4 package helpers 5 6 import ( 7 "context" 8 "fmt" 9 "io" 10 "net/http" 11 "os" 12 "path/filepath" 13 "strings" 14 15 certv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1" 16 oam "github.com/crossplane/oam-kubernetes-runtime/apis/core" 17 "github.com/spf13/cobra" 18 "github.com/verrazzano/verrazzano/pkg/k8sutil" 19 "github.com/verrazzano/verrazzano/pkg/semver" 20 v1alpha1 "github.com/verrazzano/verrazzano/platform-operator/apis/verrazzano/v1alpha1" 21 "github.com/verrazzano/verrazzano/platform-operator/apis/verrazzano/v1beta1" 22 vpoconstants "github.com/verrazzano/verrazzano/platform-operator/constants" 23 "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/registry" 24 vzconstants "github.com/verrazzano/verrazzano/tools/vz/pkg/constants" 25 "github.com/verrazzano/verrazzano/tools/vz/pkg/github" 26 adminv1 "k8s.io/api/admissionregistration/v1" 27 appv1 "k8s.io/api/apps/v1" 28 batchv1 "k8s.io/api/batch/v1" 29 corev1 "k8s.io/api/core/v1" 30 networkingv1 "k8s.io/api/networking/v1" 31 rbacv1 "k8s.io/api/rbac/v1" 32 apierrors "k8s.io/apimachinery/pkg/api/errors" 33 "k8s.io/apimachinery/pkg/api/meta" 34 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 35 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 36 "k8s.io/apimachinery/pkg/runtime" 37 "k8s.io/apimachinery/pkg/runtime/schema" 38 "k8s.io/apimachinery/pkg/types" 39 "k8s.io/client-go/discovery" 40 "k8s.io/client-go/dynamic" 41 "k8s.io/client-go/kubernetes" 42 "sigs.k8s.io/controller-runtime/pkg/client" 43 "sigs.k8s.io/yaml" 44 ) 45 46 type VZHelper interface { 47 GetOutputStream() io.Writer 48 GetErrorStream() io.Writer 49 GetInputStream() io.Reader 50 GetClient(cmd *cobra.Command) (client.Client, error) 51 GetKubeClient(cmd *cobra.Command) (kubernetes.Interface, error) 52 GetHTTPClient() *http.Client 53 GetDynamicClient(cmd *cobra.Command) (dynamic.Interface, error) 54 GetDiscoveryClient(cmd *cobra.Command) (discovery.DiscoveryInterface, error) 55 VerifyCLIArgsNil(cmd *cobra.Command) error 56 } 57 58 type ReportCtx struct { 59 ReportFile string 60 ReportFormat string 61 IncludeSupportData bool 62 IncludeInfo bool 63 IncludeActions bool 64 MinConfidence int 65 MinImpact int 66 PrintReportToConsole bool 67 } 68 69 type ClusterSnapshotCtx struct { 70 BugReportDir string 71 MoreNS []string 72 PrintReportToConsole bool 73 } 74 75 const defaultVerrazzanoTmpl = `apiVersion: install.verrazzano.io/%s 76 kind: Verrazzano 77 metadata: 78 name: verrazzano 79 namespace: default` 80 81 const v1beta1MinVersion = "1.4.0" 82 83 var ( 84 vzVer, k8sVer string 85 ) 86 87 func NewVerrazzanoForVZVersion(version string) (schema.GroupVersion, client.Object, error) { 88 if version == "" { 89 // default to a v1beta1 compatible version if not specified 90 version = "1.5.0" 91 } 92 actualVersion, err := semver.NewSemVersion(version) 93 if err != nil { 94 return schema.GroupVersion{}, nil, err 95 } 96 minVersion, err := semver.NewSemVersion(v1beta1MinVersion) 97 if err != nil { 98 return schema.GroupVersion{}, nil, err 99 } 100 if actualVersion.IsLessThan(minVersion) { 101 o, err := newVerazzanoWithAPIVersion(v1alpha1.SchemeGroupVersion.Version) 102 return v1alpha1.SchemeGroupVersion, o, err 103 } 104 o, err := newVerazzanoWithAPIVersion(v1beta1.SchemeGroupVersion.Version) 105 return v1beta1.SchemeGroupVersion, o, err 106 } 107 108 func newVerazzanoWithAPIVersion(version string) (client.Object, error) { 109 vz := &unstructured.Unstructured{} 110 if err := yaml.Unmarshal([]byte(fmt.Sprintf(defaultVerrazzanoTmpl, version)), vz); err != nil { 111 return nil, err 112 } 113 return vz, nil 114 } 115 116 func NewVerrazzanoForGroupVersion(groupVersion schema.GroupVersion) func() interface{} { 117 switch groupVersion { 118 case v1alpha1.SchemeGroupVersion: 119 return func() interface{} { 120 return &v1alpha1.Verrazzano{} 121 } 122 default: 123 return func() interface{} { 124 return &v1beta1.Verrazzano{} 125 } 126 } 127 } 128 129 // FindVerrazzanoResource - find the single Verrazzano resource 130 func FindVerrazzanoResource(client client.Client) (*v1beta1.Verrazzano, error) { 131 vzList := v1beta1.VerrazzanoList{} 132 err := client.List(context.TODO(), &vzList) 133 if err != nil { 134 // If v1beta1 resource version doesn't exist, try v1alpha1 135 if meta.IsNoMatchError(err) { 136 return findVerazzanoResourceV1Alpha1(client) 137 } 138 return nil, failedToFindResourceError(err) 139 } 140 if err := checkListLength(len(vzList.Items)); err != nil { 141 return nil, err 142 } 143 return &vzList.Items[0], nil 144 } 145 146 // GetVerrazzanoResource - get a Verrazzano resource 147 func GetVerrazzanoResource(client client.Client, namespacedName types.NamespacedName) (*v1beta1.Verrazzano, error) { 148 vz := &v1beta1.Verrazzano{} 149 if err := client.Get(context.TODO(), namespacedName, vz); err != nil { 150 if meta.IsNoMatchError(err) { 151 return getVerrazzanoResourceV1Alpha1(client, namespacedName) 152 } 153 return nil, failedToGetResourceError(err) 154 155 } 156 return vz, nil 157 } 158 159 func UpdateVerrazzanoResource(client client.Client, vz *v1beta1.Verrazzano) error { 160 err := client.Update(context.TODO(), vz) 161 // upgrade version may not support v1beta1 162 if err != nil && (meta.IsNoMatchError(err) || apierrors.IsNotFound(err)) { 163 vzV1Alpha1 := &v1alpha1.Verrazzano{} 164 err = vzV1Alpha1.ConvertFrom(vz) 165 if err != nil { 166 return err 167 } 168 return client.Update(context.TODO(), vzV1Alpha1) 169 } 170 return err 171 } 172 173 // GetLatestReleaseVersion - get the version of the latest release of Verrazzano 174 func GetLatestReleaseVersion(client *http.Client) (string, error) { 175 // Get the list of all Verrazzano releases 176 releases, err := github.ListReleases(client) 177 if err != nil { 178 return "", fmt.Errorf("Failed to get list of Verrazzano releases: %s", err.Error()) 179 } 180 return getLatestReleaseVersion(releases) 181 } 182 183 // getLatestReleaseVersion - determine which release it the latest based on semver values 184 func getLatestReleaseVersion(releases []string) (string, error) { 185 var latestRelease *semver.SemVersion 186 for _, tag := range releases { 187 tagSemver, err := semver.NewSemVersion(tag) 188 if err != nil { 189 return "", err 190 } 191 if latestRelease == nil { 192 // Initialize with the first tag 193 latestRelease = tagSemver 194 } else { 195 if tagSemver.IsGreatherThan(latestRelease) { 196 // Update the latest release found 197 latestRelease = tagSemver 198 } 199 } 200 } 201 return fmt.Sprintf("v%s", latestRelease.ToString()), nil 202 } 203 204 func NewScheme() *runtime.Scheme { 205 scheme := runtime.NewScheme() 206 _ = v1alpha1.AddToScheme(scheme) 207 _ = v1beta1.AddToScheme(scheme) 208 _ = corev1.SchemeBuilder.AddToScheme(scheme) 209 _ = adminv1.SchemeBuilder.AddToScheme(scheme) 210 _ = rbacv1.SchemeBuilder.AddToScheme(scheme) 211 _ = appv1.SchemeBuilder.AddToScheme(scheme) 212 _ = networkingv1.AddToScheme(scheme) 213 _ = oam.AddToScheme(scheme) 214 _ = batchv1.AddToScheme(scheme) 215 _ = certv1.AddToScheme(scheme) 216 return scheme 217 } 218 219 // GetNamespacesForAllComponents returns the list of unique namespaces of all the components included in the Verrazzano resource 220 func GetNamespacesForAllComponents(vz *v1beta1.Verrazzano) []string { 221 var nsList []string 222 if vz == nil { 223 return nsList 224 } 225 allComponents := getAllComponents(vz) 226 for _, eachComp := range allComponents { 227 found, comp := registry.FindComponent(eachComp) 228 if found { 229 nsList = append(nsList, comp.Namespace()) 230 } 231 } 232 if len(nsList) > 0 { 233 nsList = RemoveDuplicate(nsList) 234 } 235 return nsList 236 } 237 238 // getAllComponents returns the list of components from the Verrazzano resource 239 func getAllComponents(vzRes *v1beta1.Verrazzano) []string { 240 var compSlice = make([]string, 0) 241 242 for _, compStatusDetail := range vzRes.Status.Components { 243 if compStatusDetail.State == v1beta1.CompStateDisabled { 244 continue 245 } 246 compSlice = append(compSlice, compStatusDetail.Name) 247 } 248 return compSlice 249 } 250 251 func findVerazzanoResourceV1Alpha1(client client.Client) (*v1beta1.Verrazzano, error) { 252 vzV1Alpha1List := v1alpha1.VerrazzanoList{} 253 err := client.List(context.TODO(), &vzV1Alpha1List) 254 if err != nil { 255 return nil, failedToFindResourceError(err) 256 } 257 if err := checkListLength(len(vzV1Alpha1List.Items)); err != nil { 258 return nil, err 259 } 260 vzConverted := &v1beta1.Verrazzano{} 261 err = vzV1Alpha1List.Items[0].ConvertTo(vzConverted) 262 return vzConverted, err 263 } 264 265 func getVerrazzanoResourceV1Alpha1(client client.Client, namespacedName types.NamespacedName) (*v1beta1.Verrazzano, error) { 266 vzV1Alpha1 := &v1alpha1.Verrazzano{} 267 if err := client.Get(context.TODO(), namespacedName, vzV1Alpha1); err != nil { 268 return nil, failedToGetResourceError(err) 269 } 270 vz := &v1beta1.Verrazzano{} 271 err := vzV1Alpha1.ConvertTo(vz) 272 return vz, err 273 } 274 275 func failedToFindResourceError(err error) error { 276 return fmt.Errorf("Failed to find any Verrazzano resources: %s", err.Error()) 277 } 278 279 func failedToGetResourceError(err error) error { 280 return fmt.Errorf("Failed to get a Verrazzano install resource: %s", err.Error()) 281 } 282 283 func checkListLength(length int) error { 284 if length == 0 { 285 return fmt.Errorf("Failed to find any Verrazzano resources") 286 } 287 if length != 1 { 288 return fmt.Errorf("Expected to only find one Verrazzano resource, but found %d", length) 289 } 290 return nil 291 } 292 293 // GetOperatorYaml returns Kubernetes manifests to deploy the Verrazzano platform operator 294 // The return value is operator.yaml for releases earlier than 1.4.0 and verrazzano-platform-operator.yaml from release 1.4.0 onwards 295 func GetOperatorYaml(version string) (string, error) { 296 vzVersion, err := semver.NewSemVersion(version) 297 if err != nil { 298 return "", fmt.Errorf("invalid Verrazzano version: %v", err.Error()) 299 } 300 ver140, _ := semver.NewSemVersion("v" + vpoconstants.VerrazzanoVersion1_4_0) 301 var url string 302 if vzVersion.IsGreaterThanOrEqualTo(ver140) { 303 url = fmt.Sprintf(vzconstants.VerrazzanoPlatformOperatorURL, version) 304 } else { 305 url = fmt.Sprintf(vzconstants.VerrazzanoOperatorURL, version) 306 } 307 return url, nil 308 } 309 310 // SetK8sVer returns cluster Kubernetes version 311 func SetK8sVer() error { 312 config, err := k8sutil.GetConfigFromController() 313 if err != nil { 314 return fmt.Errorf("error getting config from the Controller Runtime: %v", err.Error()) 315 } 316 317 client, err := kubernetes.NewForConfig(config) 318 if err != nil { 319 return fmt.Errorf("error getting a clientset for the given config %v", err.Error()) 320 } 321 322 versionInfo, err := client.ServerVersion() 323 if err != nil { 324 return fmt.Errorf("error getting kubernetes version %v", err.Error()) 325 } 326 327 k8sVer = versionInfo.String() 328 return nil 329 } 330 331 // SetVzVer set verrazzano version 332 func SetVzVer(client *client.Client) error { 333 vz, vzErr := FindVerrazzanoResource(*client) 334 if vzErr != nil { 335 return vzErr 336 } 337 vzVer = vz.Status.Version 338 return nil 339 } 340 341 // GetVersionOut returns the customised k8s and vz version string 342 func GetVersionOut() string { 343 verOut := "" 344 if vzVer != "" { 345 verOut += fmt.Sprintf("\nVerrazzano Version: %s", vzVer) 346 } 347 if k8sVer != "" { 348 verOut += fmt.Sprintf("\nKubernetes Version: %s\n", k8sVer) 349 } 350 return verOut 351 } 352 353 // VerifyVzInstallNamespaceExists returns existence of verrazzano-install namespace 354 func VerifyVzInstallNamespaceExists(kubeClient kubernetes.Interface) bool { 355 pods, err := kubeClient.CoreV1().Pods(vzconstants.VerrazzanoInstall).List(context.TODO(), metav1.ListOptions{}) 356 if err != nil { 357 return false 358 } 359 for i := range pods.Items { 360 if strings.HasPrefix(pods.Items[i].Name, vzconstants.VerrazzanoPlatformOperator) { 361 return true 362 } 363 } 364 return false 365 } 366 367 // CheckAndRemoveBugReportExistsInDir checks vz bug report exists in dir or not 368 func CheckAndRemoveBugReportExistsInDir(dir string) bool { 369 bugReportFilePattern := strings.Replace(vzconstants.BugReportFileDefaultValue, "-dt", "", 1) 370 if fileMatched, _ := filepath.Glob(dir + bugReportFilePattern); len(fileMatched) == 1 { 371 os.Remove(fileMatched[0]) 372 return true 373 } 374 return false 375 }