blob: 4bd2b41bcde0fd47a058950aeae70470eb5e6704 [file] [log] [blame]
// Copyright Istio 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 mesh contains types and functions.
package mesh
import (
"fmt"
"io"
"os"
"strings"
"sync"
"time"
)
import (
"istio.io/pkg/log"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
)
import (
"github.com/apache/dubbo-go-pixiu/istioctl/pkg/install/k8sversion"
"github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1"
"github.com/apache/dubbo-go-pixiu/operator/pkg/cache"
"github.com/apache/dubbo-go-pixiu/operator/pkg/helmreconciler"
"github.com/apache/dubbo-go-pixiu/operator/pkg/manifest"
"github.com/apache/dubbo-go-pixiu/operator/pkg/name"
"github.com/apache/dubbo-go-pixiu/operator/pkg/object"
"github.com/apache/dubbo-go-pixiu/operator/pkg/util/clog"
"github.com/apache/dubbo-go-pixiu/pkg/kube"
)
var (
// installerScope is the scope for all commands in the mesh package.
installerScope = log.RegisterScope("installer", "installer", 0)
// testK8Interface is used if it is set. Not possible to inject due to cobra command boundary.
testK8Interface *kubernetes.Clientset
testRestConfig *rest.Config
)
type Printer interface {
Printf(format string, a ...interface{})
Println(string)
}
func NewPrinterForWriter(w io.Writer) Printer {
return &writerPrinter{writer: w}
}
type writerPrinter struct {
writer io.Writer
}
func (w *writerPrinter) Printf(format string, a ...interface{}) {
_, _ = fmt.Fprintf(w.writer, format, a...)
}
func (w *writerPrinter) Println(str string) {
_, _ = fmt.Fprintln(w.writer, str)
}
func initLogsOrExit(_ *RootArgs) {
if err := configLogs(log.DefaultOptions()); err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Could not configure logs: %s", err)
os.Exit(1)
}
}
var logMutex = sync.Mutex{}
func configLogs(opt *log.Options) error {
logMutex.Lock()
defer logMutex.Unlock()
op := []string{"stderr"}
opt2 := *opt
opt2.OutputPaths = op
opt2.ErrorOutputPaths = op
return log.Configure(&opt2)
}
func refreshGoldenFiles() bool {
ev := os.Getenv("REFRESH_GOLDEN")
return ev == "true" || ev == "1"
}
func kubeBuilderInstalled() bool {
ev := os.Getenv("KUBEBUILDER")
return ev == "true" || ev == "1"
}
// confirm waits for a user to confirm with the supplied message.
func confirm(msg string, writer io.Writer) bool {
_, _ = fmt.Fprintf(writer, "%s ", msg)
var response string
_, err := fmt.Scanln(&response)
if err != nil {
return false
}
response = strings.ToUpper(response)
if response == "Y" || response == "YES" {
return true
}
return false
}
func KubernetesClients(kubeConfigPath, context string, l clog.Logger) (kube.ExtendedClient, client.Client, error) {
rc, err := kube.DefaultRestConfig(kubeConfigPath, context, func(config *rest.Config) {
// We are running a one-off command locally, so we don't need to worry too much about rate limitting
// Bumping this up greatly decreases install time
config.QPS = 50
config.Burst = 100
})
if err != nil {
return nil, nil, err
}
kubeClient, err := kube.NewExtendedClient(kube.NewClientConfigForRestConfig(rc), "")
if err != nil {
return nil, nil, fmt.Errorf("create Kubernetes client: %v", err)
}
client, err := client.New(kubeClient.RESTConfig(), client.Options{Scheme: kube.IstioScheme})
if err != nil {
return nil, nil, err
}
if err := k8sversion.IsK8VersionSupported(kubeClient, l); err != nil {
return nil, nil, fmt.Errorf("check minimum supported Kubernetes version: %v", err)
}
return kubeClient, client, nil
}
// applyOptions contains the startup options for applying the manifest.
type applyOptions struct {
// Path to the kubeconfig file.
Kubeconfig string
// ComponentName of the kubeconfig context to use.
Context string
// DryRun performs all steps except actually applying the manifests or creating output dirs/files.
DryRun bool
// Maximum amount of time to wait for resources to be ready after install when Wait=true.
WaitTimeout time.Duration
}
func applyManifest(kubeClient kube.Client, client client.Client, manifestStr string,
componentName name.ComponentName, opts *applyOptions, iop *v1alpha1.IstioOperator, l clog.Logger) error {
// Needed in case we are running a test through this path that doesn't start a new process.
cache.FlushObjectCaches()
reconciler, err := helmreconciler.NewHelmReconciler(client, kubeClient, iop, &helmreconciler.Options{DryRun: opts.DryRun, Log: l})
if err != nil {
l.LogAndError(err)
return err
}
ms := name.Manifest{
Name: componentName,
Content: manifestStr,
}
_, _, err = reconciler.ApplyManifest(ms, reconciler.CheckSSAEnabled())
return err
}
// --manifests is an alias for --set installPackagePath=
// --revision is an alias for --set revision=
func applyFlagAliases(flags []string, manifestsPath, revision string) []string {
if manifestsPath != "" {
flags = append(flags, fmt.Sprintf("installPackagePath=%s", manifestsPath))
}
if revision != "" {
flags = append(flags, fmt.Sprintf("revision=%s", revision))
}
return flags
}
// getCRAndNamespaceFromFile returns the CR name and istio namespace from a file containing an IstioOperator CR.
func getCRAndNamespaceFromFile(filePath string, l clog.Logger) (customResource string, istioNamespace string, err error) {
if filePath == "" {
return "", "", nil
}
_, mergedIOPS, err := manifest.GenerateConfig([]string{filePath}, nil, false, nil, l)
if err != nil {
return "", "", err
}
b, err := os.ReadFile(filePath)
if err != nil {
return "", "", fmt.Errorf("could not read values from file %s: %s", filePath, err)
}
customResource = string(b)
istioNamespace = mergedIOPS.Namespace
return
}
// createNamespace creates a namespace using the given k8s interface.
func createNamespace(cs kubernetes.Interface, namespace string, network string, dryRun bool) error {
return helmreconciler.CreateNamespace(cs, namespace, network, dryRun)
}
// saveIOPToCluster saves the state in an IOP CR in the cluster.
func saveIOPToCluster(reconciler *helmreconciler.HelmReconciler, iop string) error {
obj, err := object.ParseYAMLToK8sObject([]byte(iop))
if err != nil {
return err
}
return reconciler.ApplyObject(obj.UnstructuredObject(), false)
}