func()

in pkg/k8s-client/eks-deprecate.go [23:753]


func (e *eks) Deprecate(batchLimit int64, batchInterval time.Duration) (err error) {
	rbPath := filepath.Join(e.cfg.Dir, "rollback.sh")
	var rbF *os.File
	rbF, err = createBashScript(rbPath)
	if err != nil {
		return err
	}
	defer func() {
		rbF.Close()
		fileutil.EnsureExecutable(rbPath)
	}()
	upPath := filepath.Join(e.cfg.Dir, "upgrade.sh")
	var upF *os.File
	upF, err = createBashScript(upPath)
	if err != nil {
		return err
	}
	defer func() {
		upF.Close()
		fileutil.EnsureExecutable(upPath)
	}()

	getCmd := []string{e.cfg.KubectlPath, "--kubeconfig=" + e.cfg.KubeConfigPath, "get", "all"}
	_, err = rbF.Write([]byte(strings.Join(getCmd, " ") + "\n\n"))
	if err != nil {
		return err
	}
	_, err = upF.Write([]byte(strings.Join(getCmd, " ") + "\n\n"))
	if err != nil {
		return err
	}

	ver, err := e.fetchServerVersion()
	if err != nil {
		return err
	}
	verTxt, err := json.MarshalIndent(ver, "", "    ")
	if err != nil {
		return err
	}

	cur := ver.VersionValue
	new := cur + 0.01

	apis, err := deprecate.APIs(new)
	if err != nil {
		e.cfg.Logger.Warn("version not supported", zap.Error(err))
		return nil
	}
	deprecates := make([]string, 0, len(apis))
	for k := range apis {
		deprecates = append(deprecates, fmt.Sprintf("%s.%s", k.APIVersion, k.Kind))
	}
	sort.Strings(deprecates)

	ns, err := e.listNamespaces(20, time.Second)
	if err != nil {
		return err
	}
	namespaces := make([]string, 0, len(ns))
	for _, nv := range ns {
		namespaces = append(namespaces, nv.GetName())
	}
	sort.Strings(namespaces)

	e.cfg.Logger.Info("😎 🙏 🚶 ✔️ 👍  checking deprecated APIs",
		zap.Bool("enable-prompt", e.cfg.EnablePrompt),
		zap.Int64("batch-limit", batchLimit),
		zap.Duration("batch-interval", batchInterval),
		zap.String("rollback-script", rbPath),
		zap.String("upgrade-script", upPath),
		zap.String("version-current", fmt.Sprintf("%.2f", cur)),
		zap.String("version-target", fmt.Sprintf("%.2f", new)),
		zap.Strings("namespaces", namespaces),
		zap.Strings("deprecates", deprecates),
	)
	fmt.Printf("\n%s\n\n", string(verTxt))

	if e.cfg.EnablePrompt {
		prompt := promptui.Select{
			Label: "Ready to list all resources to find deprecated APIs, should we continue?",
			Items: []string{
				"No, stop it!",
				"Yes, let's find them all!",
			},
		}
		idx, answer, err := prompt.Run()
		if err != nil {
			return err
		}
		if idx != 1 {
			e.cfg.Logger.Info("returning", zap.Int("index", idx), zap.String("answer", answer))
			return nil
		}
	}

	// TODO: this runs highly redundant queries... optimize...
	//  1. find all resources with the "Namespace" and "Kind"
	//  2. decide whether to deprecate based on "kubectl get -o=yaml"
	fmt.Printf("\n\n************************\n")
	e.cfg.Logger.Info("listing all resources to find deprecated APIs")
	for from, to := range apis {
		fmt.Printf("\n\n************************\n")
		switch {
		case from.APIVersion == "apps/v1beta1" && from.Kind == "Deployment",
			from.APIVersion == "apps/v1beta2" && from.Kind == "Deployment",
			from.APIVersion == "extensions/v1beta1" && from.Kind == "Deployment":
			for _, namespace := range namespaces {
				e.cfg.Logger.Info("checking",
					zap.String("from-api-version", from.APIVersion),
					zap.String("from-kind", from.Kind),
					zap.String("to-api-version", to.APIVersion),
					zap.String("to-kind", to.Kind),
					zap.String("namespace", namespace),
				)

				rs1, err := e.ListAppsV1beta1Deployments(namespace, batchLimit, batchInterval)
				if err != nil {
					return err
				}
				rs2, err := e.ListAppsV1beta2Deployments(namespace, batchLimit, batchInterval)
				if err != nil {
					return err
				}
				rs3, err := e.ListAppsV1Deployments(namespace, batchLimit, batchInterval)
				if err != nil {
					return err
				}
				rs4, err := e.ListExtensionsV1beta1Deployments(namespace, batchLimit, batchInterval)
				if err != nil {
					return err
				}

				if len(rs1) == 0 && len(rs2) == 0 && len(rs3) == 0 && len(rs4) == 0 {
					e.cfg.Logger.Info("😁 😁 😁  skipping; no resource found",
						zap.String("from-api-version", from.APIVersion),
						zap.String("from-kind", from.Kind),
						zap.String("namespace", namespace),
					)
					continue
				}
				resources := make(map[string]struct{})
				for _, v := range rs1 {
					resources[v.ObjectMeta.Name] = struct{}{}
				}
				for _, v := range rs2 {
					resources[v.ObjectMeta.Name] = struct{}{}
				}
				for _, v := range rs3 {
					resources[v.ObjectMeta.Name] = struct{}{}
				}
				for _, v := range rs4 {
					resources[v.ObjectMeta.Name] = struct{}{}
				}
				allNames := make([]string, 0, len(resources))
				for k := range resources {
					allNames = append(allNames, k)
				}
				sort.Strings(allNames)
				e.cfg.Logger.Info("checking all names", zap.String("namespace", namespace), zap.Strings("names", allNames))
				for _, name := range allNames {
					time.Sleep(100 * time.Millisecond)

					orig, origBody, err := e.GetObject(namespace, from.Kind, name)
					if err != nil {
						return err
					}

					if orig.APIVersion == "" || orig.APIVersion == to.APIVersion {
						e.cfg.Logger.Warn("😁  skipping latest API",
							zap.String("namespace", namespace),
							zap.String("name", name),
							zap.String("current-api-version", orig.APIVersion),
							zap.String("expected-api-version", to.APIVersion),
						)
						time.Sleep(batchInterval)
						continue
					}

					e.cfg.Logger.Warn("🔥 💀 👽 😱 😡  found deprecated API!",
						zap.String("namespace", namespace),
						zap.String("name", name),
						zap.String("current-api-version", orig.APIVersion),
						zap.String("expected-api-version", to.APIVersion),
					)

					if err = e.saveKubectlGet(namespace, orig.Kind, name, rbF, "\n"); err != nil {
						return err
					}
					if err = e.saveKubectlGet(namespace, orig.Kind, name, upF, "\n"); err != nil {
						return err
					}
					patchBody := strings.Replace(
						string(origBody),
						"apiVersion: "+orig.APIVersion+"\n",
						"apiVersion: "+to.APIVersion+"\n",
						1,
					)

					origYAMLPath, err := e.saveYAML(namespace, orig.APIVersion, orig.Kind, name, ".original.yaml", origBody)
					if err != nil {
						return err
					}
					patchYAMLPath, err := e.saveYAML(namespace, to.APIVersion, to.Kind, name, ".patch.yaml", []byte(patchBody))
					if err != nil {
						return err
					}

					if err = e.saveKubectlApply(origYAMLPath, rbF, "\n\n"); err != nil {
						return err
					}
					if err = e.saveKubectlConvert(namespace, from.Kind, to.APIVersion, name, upF, "\n"); err != nil {
						return err
					}
					if err = e.saveKubectlApply(patchYAMLPath, upF, "\n\n"); err != nil {
						return err
					}
				}
			}

		case from.APIVersion == "apps/v1beta1" && from.Kind == "StatefulSet",
			from.APIVersion == "apps/v1beta2" && from.Kind == "StatefulSet":
			for _, namespace := range namespaces {
				e.cfg.Logger.Info("checking",
					zap.String("from-api-version", from.APIVersion),
					zap.String("from-kind", from.Kind),
					zap.String("to-api-version", to.APIVersion),
					zap.String("to-kind", to.Kind),
					zap.String("namespace", namespace),
				)

				rs1, err := e.ListAppsV1beta1StatefulSets(namespace, batchLimit, batchInterval)
				if err != nil {
					return err
				}
				rs2, err := e.ListAppsV1beta2StatefulSets(namespace, batchLimit, batchInterval)
				if err != nil {
					return err
				}
				rs3, err := e.ListAppsV1StatefulSets(namespace, batchLimit, batchInterval)
				if err != nil {
					return err
				}

				if len(rs1) == 0 && len(rs2) == 0 && len(rs3) == 0 {
					e.cfg.Logger.Info("😁 😁 😁  skipping; no resource found",
						zap.String("from-api-version", from.APIVersion),
						zap.String("from-kind", from.Kind),
						zap.String("namespace", namespace),
					)
					time.Sleep(batchInterval)
					continue
				}
				resources := make(map[string]struct{})
				for _, v := range rs1 {
					resources[v.ObjectMeta.Name] = struct{}{}
				}
				for _, v := range rs2 {
					resources[v.ObjectMeta.Name] = struct{}{}
				}
				for _, v := range rs3 {
					resources[v.ObjectMeta.Name] = struct{}{}
				}
				allNames := make([]string, 0, len(resources))
				for k := range resources {
					allNames = append(allNames, k)
				}
				sort.Strings(allNames)
				e.cfg.Logger.Info("checking all names", zap.String("namespace", namespace), zap.Strings("names", allNames))
				for _, name := range allNames {
					time.Sleep(100 * time.Millisecond)

					orig, origBody, err := e.GetObject(namespace, from.Kind, name)
					if err != nil {
						return err
					}

					if orig.APIVersion == "" || orig.APIVersion == to.APIVersion {
						e.cfg.Logger.Warn("😁  skipping latest API",
							zap.String("namespace", namespace),
							zap.String("name", name),
							zap.String("current-api-version", orig.APIVersion),
							zap.String("expected-api-version", to.APIVersion),
						)
						continue
					}

					e.cfg.Logger.Warn("🔥 💀 👽 😱 😡  found deprecated API!",
						zap.String("namespace", namespace),
						zap.String("name", name),
						zap.String("current-api-version", orig.APIVersion),
						zap.String("expected-api-version", to.APIVersion),
					)

					if err = e.saveKubectlGet(namespace, orig.Kind, name, rbF, "\n"); err != nil {
						return err
					}
					if err = e.saveKubectlGet(namespace, orig.Kind, name, upF, "\n"); err != nil {
						return err
					}
					patchBody := strings.Replace(
						string(origBody),
						"apiVersion: "+orig.APIVersion+"\n",
						"apiVersion: "+to.APIVersion+"\n",
						1,
					)

					origYAMLPath, err := e.saveYAML(namespace, orig.APIVersion, orig.Kind, name, ".original.yaml", origBody)
					if err != nil {
						return err
					}
					patchYAMLPath, err := e.saveYAML(namespace, to.APIVersion, to.Kind, name, ".patch.yaml", []byte(patchBody))
					if err != nil {
						return err
					}

					if err = e.saveKubectlApply(origYAMLPath, rbF, "\n\n"); err != nil {
						return err
					}
					if err = e.saveKubectlConvert(namespace, from.Kind, to.APIVersion, name, upF, "\n"); err != nil {
						return err
					}
					if err = e.saveKubectlApply(patchYAMLPath, upF, "\n\n"); err != nil {
						return err
					}
				}
			}

		case from.APIVersion == "extensions/v1beta1" && from.Kind == "DaemonSet":
			for _, namespace := range namespaces {
				e.cfg.Logger.Info("checking",
					zap.String("from-api-version", from.APIVersion),
					zap.String("from-kind", from.Kind),
					zap.String("to-api-version", to.APIVersion),
					zap.String("to-kind", to.Kind),
					zap.String("namespace", namespace),
				)

				rs1, err := e.ListExtensionsV1beta1DaemonSets(namespace, batchLimit, batchInterval)
				if err != nil {
					return err
				}
				time.Sleep(batchInterval)
				rs2, err := e.ListAppsV1DaemonSets(namespace, batchLimit, batchInterval)
				if err != nil {
					return err
				}

				if len(rs1) == 0 && len(rs2) == 0 {
					e.cfg.Logger.Info("😁 😁 😁  skipping; no resource found",
						zap.String("from-api-version", from.APIVersion),
						zap.String("from-kind", from.Kind),
						zap.String("namespace", namespace),
					)
					time.Sleep(batchInterval)
					continue
				}
				resources := make(map[string]struct{})
				for _, v := range rs1 {
					resources[v.ObjectMeta.Name] = struct{}{}
				}
				for _, v := range rs2 {
					resources[v.ObjectMeta.Name] = struct{}{}
				}
				allNames := make([]string, 0, len(resources))
				for k := range resources {
					allNames = append(allNames, k)
				}
				sort.Strings(allNames)
				e.cfg.Logger.Info("checking all names", zap.String("namespace", namespace), zap.Strings("names", allNames))
				for _, name := range allNames {
					time.Sleep(100 * time.Millisecond)

					orig, origBody, err := e.GetObject(namespace, from.Kind, name)
					if err != nil {
						return err
					}

					if orig.APIVersion == "" || orig.APIVersion == to.APIVersion {
						e.cfg.Logger.Warn("😁  skipping latest API",
							zap.String("namespace", namespace),
							zap.String("name", name),
							zap.String("current-api-version", orig.APIVersion),
							zap.String("expected-api-version", to.APIVersion),
						)
						continue
					}

					e.cfg.Logger.Warn("🔥 💀 👽 😱 😡  found deprecated API!",
						zap.String("namespace", namespace),
						zap.String("name", name),
						zap.String("current-api-version", orig.APIVersion),
						zap.String("expected-api-version", to.APIVersion),
					)

					if err = e.saveKubectlGet(namespace, orig.Kind, name, rbF, "\n"); err != nil {
						return err
					}
					if err = e.saveKubectlGet(namespace, orig.Kind, name, upF, "\n"); err != nil {
						return err
					}
					patchBody := strings.Replace(
						string(origBody),
						"apiVersion: "+orig.APIVersion+"\n",
						"apiVersion: "+to.APIVersion+"\n",
						1,
					)

					origYAMLPath, err := e.saveYAML(namespace, orig.APIVersion, orig.Kind, name, ".original.yaml", origBody)
					if err != nil {
						return err
					}
					patchYAMLPath, err := e.saveYAML(namespace, to.APIVersion, to.Kind, name, ".patch.yaml", []byte(patchBody))
					if err != nil {
						return err
					}

					if err = e.saveKubectlApply(origYAMLPath, rbF, "\n\n"); err != nil {
						return err
					}
					if err = e.saveKubectlConvert(namespace, from.Kind, to.APIVersion, name, upF, "\n"); err != nil {
						return err
					}
					if err = e.saveKubectlApply(patchYAMLPath, upF, "\n\n"); err != nil {
						return err
					}
				}
			}

		case from.APIVersion == "extensions/v1beta1" && from.Kind == "ReplicaSet":
			for _, namespace := range namespaces {
				e.cfg.Logger.Info("checking",
					zap.String("from-api-version", from.APIVersion),
					zap.String("from-kind", from.Kind),
					zap.String("to-api-version", to.APIVersion),
					zap.String("to-kind", to.Kind),
					zap.String("namespace", namespace),
				)

				rs1, err := e.ListExtensionsV1beta1ReplicaSets(namespace, batchLimit, batchInterval)
				if err != nil {
					return err
				}
				time.Sleep(batchInterval)
				rs2, err := e.ListAppsV1ReplicaSets(namespace, batchLimit, batchInterval)
				if err != nil {
					return err
				}

				if len(rs1) == 0 && len(rs2) == 0 {
					e.cfg.Logger.Info("😁 😁 😁  skipping; no resource found",
						zap.String("from-api-version", from.APIVersion),
						zap.String("from-kind", from.Kind),
						zap.String("namespace", namespace),
					)
					time.Sleep(batchInterval)
					continue
				}
				resources := make(map[string]struct{})
				for _, v := range rs1 {
					resources[v.ObjectMeta.Name] = struct{}{}
				}
				for _, v := range rs2 {
					resources[v.ObjectMeta.Name] = struct{}{}
				}
				allNames := make([]string, 0, len(resources))
				for k := range resources {
					allNames = append(allNames, k)
				}
				sort.Strings(allNames)
				e.cfg.Logger.Info("checking all names", zap.String("namespace", namespace), zap.Strings("names", allNames))
				for _, name := range allNames {
					time.Sleep(100 * time.Millisecond)

					orig, origBody, err := e.GetObject(namespace, from.Kind, name)
					if err != nil {
						return err
					}

					if orig.APIVersion == "" || orig.APIVersion == to.APIVersion {
						e.cfg.Logger.Warn("😁  skipping latest API",
							zap.String("namespace", namespace),
							zap.String("name", name),
							zap.String("current-api-version", orig.APIVersion),
							zap.String("expected-api-version", to.APIVersion),
						)
						continue
					}

					e.cfg.Logger.Warn("🔥 💀 👽 😱 😡  found deprecated API!",
						zap.String("namespace", namespace),
						zap.String("name", name),
						zap.String("current-api-version", orig.APIVersion),
						zap.String("expected-api-version", to.APIVersion),
					)

					if err = e.saveKubectlGet(namespace, orig.Kind, name, rbF, "\n"); err != nil {
						return err
					}
					if err = e.saveKubectlGet(namespace, orig.Kind, name, upF, "\n"); err != nil {
						return err
					}
					patchBody := strings.Replace(
						string(origBody),
						"apiVersion: "+orig.APIVersion+"\n",
						"apiVersion: "+to.APIVersion+"\n",
						1,
					)

					origYAMLPath, err := e.saveYAML(namespace, orig.APIVersion, orig.Kind, name, ".original.yaml", origBody)
					if err != nil {
						return err
					}
					patchYAMLPath, err := e.saveYAML(namespace, to.APIVersion, to.Kind, name, ".patch.yaml", []byte(patchBody))
					if err != nil {
						return err
					}

					if err = e.saveKubectlApply(origYAMLPath, rbF, "\n\n"); err != nil {
						return err
					}
					if err = e.saveKubectlConvert(namespace, from.Kind, to.APIVersion, name, upF, "\n"); err != nil {
						return err
					}
					if err = e.saveKubectlApply(patchYAMLPath, upF, "\n\n"); err != nil {
						return err
					}
				}
			}

		case from.APIVersion == "extensions/v1beta1" && from.Kind == "NetworkPolicy":
			for _, namespace := range namespaces {
				e.cfg.Logger.Info("checking",
					zap.String("from-api-version", from.APIVersion),
					zap.String("from-kind", from.Kind),
					zap.String("to-api-version", to.APIVersion),
					zap.String("to-kind", to.Kind),
					zap.String("namespace", namespace),
				)

				rs1, err := e.ListExtensionsV1beta1NetworkPolicies(namespace, batchLimit, batchInterval)
				if err != nil {
					return err
				}
				rs2, err := e.ListNetworkingV1NetworkPolicies(namespace, batchLimit, batchInterval)
				if err != nil {
					return err
				}

				if len(rs1) == 0 && len(rs2) == 0 {
					e.cfg.Logger.Info("😁 😁 😁  skipping; no resource found",
						zap.String("from-api-version", from.APIVersion),
						zap.String("from-kind", from.Kind),
						zap.String("namespace", namespace),
					)
					time.Sleep(batchInterval)
					continue
				}
				resources := make(map[string]struct{})
				for _, v := range rs1 {
					resources[v.ObjectMeta.Name] = struct{}{}
				}
				for _, v := range rs2 {
					resources[v.ObjectMeta.Name] = struct{}{}
				}
				allNames := make([]string, 0, len(resources))
				for k := range resources {
					allNames = append(allNames, k)
				}
				sort.Strings(allNames)
				e.cfg.Logger.Info("checking all names", zap.String("namespace", namespace), zap.Strings("names", allNames))
				for _, name := range allNames {
					time.Sleep(100 * time.Millisecond)

					orig, origBody, err := e.GetObject(namespace, from.Kind, name)
					if err != nil {
						return err
					}

					if orig.APIVersion == "" || orig.APIVersion == to.APIVersion {
						e.cfg.Logger.Warn("😁  skipping latest API",
							zap.String("namespace", namespace),
							zap.String("name", name),
							zap.String("current-api-version", orig.APIVersion),
							zap.String("expected-api-version", to.APIVersion),
						)
						continue
					}

					e.cfg.Logger.Warn("🔥 💀 👽 😱 😡  found deprecated API!",
						zap.String("namespace", namespace),
						zap.String("name", name),
						zap.String("current-api-version", orig.APIVersion),
						zap.String("expected-api-version", to.APIVersion),
					)

					if err = e.saveKubectlGet(namespace, orig.Kind, name, rbF, "\n"); err != nil {
						return err
					}
					if err = e.saveKubectlGet(namespace, orig.Kind, name, upF, "\n"); err != nil {
						return err
					}
					patchBody := strings.Replace(
						string(origBody),
						"apiVersion: "+orig.APIVersion+"\n",
						"apiVersion: "+to.APIVersion+"\n",
						1,
					)

					origYAMLPath, err := e.saveYAML(namespace, orig.APIVersion, orig.Kind, name, ".original.yaml", origBody)
					if err != nil {
						return err
					}
					patchYAMLPath, err := e.saveYAML(namespace, to.APIVersion, to.Kind, name, ".patch.yaml", []byte(patchBody))
					if err != nil {
						return err
					}

					if err = e.saveKubectlApply(origYAMLPath, rbF, "\n\n"); err != nil {
						return err
					}
					if err = e.saveKubectlConvert(namespace, from.Kind, to.APIVersion, name, upF, "\n"); err != nil {
						return err
					}
					if err = e.saveKubectlApply(patchYAMLPath, upF, "\n\n"); err != nil {
						return err
					}
				}
			}

		case from.APIVersion == "extensions/v1beta1" && from.Kind == "PodSecurityPolicy":
			e.cfg.Logger.Info("checking",
				zap.String("from-api-version", from.APIVersion),
				zap.String("from-kind", from.Kind),
				zap.String("to-api-version", to.APIVersion),
				zap.String("to-kind", to.Kind),
			)

			rs1, err := e.ListExtensionsV1beta1PodSecurityPolicies(batchLimit, batchInterval)
			if err != nil {
				return err
			}
			rs2, err := e.ListPolicyV1beta1PodSecurityPolicies(batchLimit, batchInterval)
			if err != nil {
				return err
			}

			if len(rs1) == 0 && len(rs2) == 0 {
				e.cfg.Logger.Info("😁 😁 😁  skipping; no resource found",
					zap.String("from-api-version", from.APIVersion),
					zap.String("from-kind", from.Kind),
				)
				time.Sleep(batchInterval)
				continue
			}
			resources := make(map[string]struct{})
			for _, v := range rs1 {
				resources[v.ObjectMeta.Name] = struct{}{}
			}
			for _, v := range rs2 {
				resources[v.ObjectMeta.Name] = struct{}{}
			}
			allNames := make([]string, 0, len(resources))
			for k := range resources {
				allNames = append(allNames, k)
			}
			sort.Strings(allNames)
			e.cfg.Logger.Info("checking all names", zap.Strings("names", allNames))
			for _, name := range allNames {
				time.Sleep(100 * time.Millisecond)

				orig, origBody, err := e.GetObject("", from.Kind, name)
				if err != nil {
					return err
				}

				if orig.APIVersion == "" || orig.APIVersion == to.APIVersion {
					e.cfg.Logger.Warn("😁  skipping latest API",
						zap.String("name", name),
						zap.String("current-api-version", orig.APIVersion),
						zap.String("expected-api-version", to.APIVersion),
					)
					continue
				}

				e.cfg.Logger.Warn("🔥 💀 👽 😱 😡  found deprecated API!",
					zap.String("name", name),
					zap.String("current-api-version", orig.APIVersion),
					zap.String("expected-api-version", to.APIVersion),
				)

				if err = e.saveKubectlGet("", orig.Kind, name, rbF, "\n"); err != nil {
					return err
				}
				if err = e.saveKubectlGet("", orig.Kind, name, upF, "\n"); err != nil {
					return err
				}
				patchBody := strings.Replace(
					string(origBody),
					"apiVersion: "+orig.APIVersion+"\n",
					"apiVersion: "+to.APIVersion+"\n",
					1,
				)

				origYAMLPath, err := e.saveYAML("", orig.APIVersion, orig.Kind, name, ".original.yaml", origBody)
				if err != nil {
					return err
				}
				patchYAMLPath, err := e.saveYAML("", to.APIVersion, to.Kind, name, ".patch.yaml", []byte(patchBody))
				if err != nil {
					return err
				}

				if err = e.saveKubectlApply(origYAMLPath, rbF, "\n\n"); err != nil {
					return err
				}
				if err = e.saveKubectlConvert("namespace", from.Kind, to.APIVersion, name, upF, "\n"); err != nil {
					return err
				}
				if err = e.saveKubectlApply(patchYAMLPath, upF, "\n\n"); err != nil {
					return err
				}
			}

		default:
			return fmt.Errorf("upgrade operation not implemented for %q %q", from.APIVersion, from.Kind)
		}
	}

	return nil
}