diff --git a/pkg/cmd/download.go b/pkg/cmd/download.go index 39a697a0b..9ce4f05ff 100644 --- a/pkg/cmd/download.go +++ b/pkg/cmd/download.go @@ -58,6 +58,15 @@ func downloadTerraformFiles(option string) string { namespace := "" var target Target ReadTarget(pathTarget, &target) + // return path allow non operator download key file + if getRole() == "user" { + gardenName := target.Stack()[0].Name + projectName := target.Stack()[1].Name + shootName := target.Stack()[2].Name + pathTerraform := filepath.Join(pathGardenHome, "cache", gardenName, "projects", projectName, shootName) + return filepath.Join(pathGardenHome, pathTerraform) + } + var err error Client, err = clientToTarget("garden") checkError(err) @@ -132,7 +141,7 @@ func downloadTerraformFiles(option string) string { checkError(err) err = ioutil.WriteFile(filepath.Join(pathGardenHome, pathTerraform, "terraform.tfvars"), []byte(secret.Data["terraform.tfvars"]), 0644) checkError(err) - return (filepath.Join(pathGardenHome, pathTerraform)) + return filepath.Join(pathGardenHome, pathTerraform) } func downloadLogs(option string) { diff --git a/pkg/cmd/miscellaneous.go b/pkg/cmd/miscellaneous.go index b7b8e612b..a76e0ef8f 100644 --- a/pkg/cmd/miscellaneous.go +++ b/pkg/cmd/miscellaneous.go @@ -15,7 +15,6 @@ package cmd import ( - "bufio" "errors" "flag" "fmt" @@ -118,32 +117,6 @@ func clientToTarget(target TargetKind) (*k8s.Clientset, error) { return clientset, err } -// getShootClusterName returns the clustername of the shoot cluster -func getShootClusterName() (clustername string) { - clustername = "" - file, _ := os.Open(getKubeConfigOfCurrentTarget()) - defer file.Close() - scanner := bufio.NewScanner(file) - scanner.Split(bufio.ScanLines) - for scanner.Scan() { - if strings.Contains(scanner.Text(), "current-context:") { - clustername = strings.TrimPrefix(scanner.Text(), "current-context: ") - } - } - // retrieve full clustername - Client, err := clientToTarget("seed") - checkError(err) - namespaces, err := Client.CoreV1().Namespaces().List(metav1.ListOptions{}) - checkError(err) - for _, namespace := range namespaces.Items { - if strings.HasSuffix(namespace.Name, clustername) { - clustername = namespace.Name - break - } - } - return clustername -} - // getMonitoringCredentials returns username and password required for url login to the montiring tools func getMonitoringCredentials() (username, password string) { var target Target @@ -358,3 +331,35 @@ func getPublicIP() string { checkError(err) return string(ip) } + +// get role either user or operator +func getRole() string { + var role string + // will refactor in https://github.com/gardener/gardenctl/issues/266 + KUBECONFIG = getKubeConfigOfClusterType("garden") + out, err := ExecCmdReturnOutput("kubectl", "--kubeconfig="+KUBECONFIG, "auth", "can-i", "get", "secret", "-A") + if err == nil { + if out == "yes" { + role = "operator" + } + } else { + role = "user" + } + return role +} + +/* +getTechnicalID returns the Technical Id, which used as clustername of the shoot cluster +*/ +func getTechnicalID() string { + var target Target + ReadTarget(pathTarget, &target) + + gardenClientset, err := target.GardenerClient() + checkError(err) + project, err := gardenClientset.CoreV1beta1().Projects().Get(target.Stack()[1].Name, metav1.GetOptions{}) + checkError(err) + shoot, err := gardenClientset.CoreV1beta1().Shoots(*project.Spec.Namespace).Get(target.Stack()[2].Name, metav1.GetOptions{}) + checkError(err) + return shoot.Status.TechnicalID +} diff --git a/pkg/cmd/operate.go b/pkg/cmd/operate.go index fa2644e41..dfc7843e7 100644 --- a/pkg/cmd/operate.go +++ b/pkg/cmd/operate.go @@ -24,8 +24,8 @@ import ( openstackinstall "github.com/gardener/gardener-extension-provider-openstack/pkg/apis/openstack/install" openstackv1alpha1 "github.com/gardener/gardener-extension-provider-openstack/pkg/apis/openstack/v1alpha1" gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" - gardencoreclientset "github.com/gardener/gardener/pkg/client/core/clientset/versioned" "github.com/jmoiron/jsonq" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/serializer" @@ -33,31 +33,34 @@ import ( // operate executes a command on specified cli with pulled credentials for target func operate(provider, arguments string) { - secretName, region := "", "" - namespaceSecret := "" - profile := "" + secretName, region, namespaceSecret, profile := "", "", "", "" var target Target ReadTarget(pathTarget, &target) var err error + var secret *v1.Secret Client, err = clientToTarget("garden") checkError(err) - gardenClientset, err := gardencoreclientset.NewForConfig(NewConfigFromBytes(*kubeconfig)) + + gardenClientset, err := target.GardenerClient() checkError(err) - shootList, err := gardenClientset.CoreV1beta1().Shoots("").List(metav1.ListOptions{}) + + project, err := gardenClientset.CoreV1beta1().Projects().Get(target.Stack()[1].Name, metav1.GetOptions{}) checkError(err) - for _, shoot := range shootList.Items { - if shoot.Name == target.Target[2].Name && strings.HasSuffix(shoot.Namespace, target.Target[1].Name) { - secretBindingName := shoot.Spec.SecretBindingName - region = shoot.Spec.Region - namespaceSecretBinding := shoot.Namespace - profile = shoot.Spec.CloudProfileName - secretBinding, err := gardenClientset.CoreV1beta1().SecretBindings(namespaceSecretBinding).Get((secretBindingName), metav1.GetOptions{}) - checkError(err) - secretName = secretBinding.SecretRef.Name - namespaceSecret = secretBinding.SecretRef.Namespace - } - } - secret, err := Client.CoreV1().Secrets(namespaceSecret).Get((secretName), metav1.GetOptions{}) + shoot, err := gardenClientset.CoreV1beta1().Shoots(*project.Spec.Namespace).Get(target.Stack()[2].Name, metav1.GetOptions{}) + checkError(err) + + secretBindingName := shoot.Spec.SecretBindingName + region = shoot.Spec.Region + namespaceSecretBinding := shoot.Namespace + profile = shoot.Spec.CloudProfileName + + secretBinding, err := gardenClientset.CoreV1beta1().SecretBindings(namespaceSecretBinding).Get((secretBindingName), metav1.GetOptions{}) + checkError(err) + + secretName = secretBinding.SecretRef.Name + namespaceSecret = secretBinding.SecretRef.Namespace + + secret, err = Client.CoreV1().Secrets(namespaceSecret).Get((secretName), metav1.GetOptions{}) checkError(err) switch provider { @@ -65,9 +68,7 @@ func operate(provider, arguments string) { accessKeyID := []byte(secret.Data["accessKeyID"]) secretAccessKey := []byte(secret.Data["secretAccessKey"]) err := ExecCmd(nil, arguments, false, "AWS_ACCESS_KEY_ID="+string(accessKeyID[:]), "AWS_SECRET_ACCESS_KEY="+string(secretAccessKey[:]), "AWS_DEFAULT_REGION="+region, "AWS_DEFAULT_OUTPUT=text") - if err != nil { - os.Exit(2) - } + checkError(err) case "gcp": serviceaccount := []byte(secret.Data["serviceaccount.json"]) data := map[string]interface{}{} @@ -76,7 +77,7 @@ func operate(provider, arguments string) { tmpFile, err := ioutil.TempFile(os.TempDir(), "tmpFile-") checkError(err) defer os.Remove(tmpFile.Name()) - _, err = tmpFile.Write(serviceaccount) + _, err = tmpFile.Write(serviceaccount) checkError(err) err = tmpFile.Close() checkError(err) diff --git a/pkg/cmd/show.go b/pkg/cmd/show.go index 68c771cd2..f90966e25 100644 --- a/pkg/cmd/show.go +++ b/pkg/cmd/show.go @@ -253,7 +253,7 @@ func showVpnShoot() { func showPrometheus() { username, password = getMonitoringCredentials() showPod("prometheus", "seed") - output, err := ExecCmdReturnOutput("bash", "-c", "export KUBECONFIG="+KUBECONFIG+"; kubectl get ingress prometheus -n "+getShootClusterName()) + output, err := ExecCmdReturnOutput("bash", "-c", "export KUBECONFIG="+KUBECONFIG+"; kubectl get ingress prometheus -n "+getTechnicalID()) if err != nil { fmt.Println("Cmd was unsuccessful") os.Exit(2) @@ -275,7 +275,7 @@ func showPrometheus() { func showAltermanager() { username, password = getMonitoringCredentials() showPod("alertmanager", "seed") - output, err := ExecCmdReturnOutput("bash", "-c", "export KUBECONFIG="+KUBECONFIG+"; kubectl get ingress alertmanager -n "+getShootClusterName()) + output, err := ExecCmdReturnOutput("bash", "-c", "export KUBECONFIG="+KUBECONFIG+"; kubectl get ingress alertmanager -n "+getTechnicalID()) if err != nil { fmt.Println("Cmd was unsuccessful") os.Exit(2) @@ -352,7 +352,7 @@ func showKubernetesDashboard() { func showGrafana() { username, password = getMonitoringCredentials() showPod("grafana", "seed") - output, err := ExecCmdReturnOutput("bash", "-c", "export KUBECONFIG="+KUBECONFIG+"; kubectl get ingress grafana -n "+getShootClusterName()) + output, err := ExecCmdReturnOutput("bash", "-c", "export KUBECONFIG="+KUBECONFIG+"; kubectl get ingress grafana -n "+getTechnicalID()) if err != nil { fmt.Println("Cmd was unsuccessful") os.Exit(2) @@ -382,7 +382,7 @@ func showKibana() { if len(target.Target) == 2 { namespace = "garden" } else if len(target.Target) == 3 { - namespace = getShootClusterName() + namespace = getTechnicalID() } output, err := ExecCmdReturnOutput("bash", "-c", "export KUBECONFIG="+KUBECONFIG+"; kubectl get ingress kibana -n "+namespace) diff --git a/pkg/cmd/ssh.go b/pkg/cmd/ssh.go index 350950c18..a6d55fba4 100644 --- a/pkg/cmd/ssh.go +++ b/pkg/cmd/ssh.go @@ -18,6 +18,7 @@ import ( "errors" "fmt" "io/ioutil" + "os" "path/filepath" gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" @@ -49,9 +50,7 @@ func NewSSHCmd(reader TargetReader, ioStreams IOStreams) *cobra.Command { } shoot, err := FetchShootFromTarget(target) - if err != nil { - return err - } + checkError(err) if len(args) == 0 { return printNodeNames(shoot.Name) @@ -121,9 +120,7 @@ func getSSHKeypair(shoot *gardencorev1beta1.Shoot) *v1.Secret { // printNodeNames print all nodes in k8s cluster func printNodeNames(shootName string) error { machineList, err := getMachineList(shootName) - if err != nil { - return err - } + checkError(err) fmt.Println("Nodes:") for _, machine := range machineList.Items { @@ -147,20 +144,27 @@ echo "gardener ALL=(ALL) NOPASSWD:ALL" >/etc/sudoers.d/99-gardener-user } func getMachineList(shootName string) (*v1alpha1.MachineList, error) { - config, err := clientcmd.BuildConfigFromFlags("", getKubeConfigOfClusterType("seed")) - if err != nil { - return nil, err + if getRole() == "user" { + var target Target + ReadTarget(pathTarget, &target) + clientset, err := target.K8SClientToKind("shoot") + checkError(err) + fmt.Printf("%s\n", "Nodes") + list, _ := clientset.CoreV1().Nodes().List(metav1.ListOptions{}) + for _, node := range list.Items { + fmt.Printf("%s\n", node.Name) + } + os.Exit(0) } + + config, err := clientcmd.BuildConfigFromFlags("", getKubeConfigOfClusterType("seed")) + checkError(err) client, err := mcmv1alpha1.NewForConfig(config) - if err != nil { - return nil, err - } + checkError(err) shootNamespace := getSeedNamespaceNameForShoot(shootName) machines, err := client.MachineV1alpha1().Machines(shootNamespace).List(metav1.ListOptions{}) - if err != nil { - return nil, err - } + checkError(err) return machines, nil } diff --git a/pkg/cmd/ssh_alicloud.go b/pkg/cmd/ssh_alicloud.go index d70f150c2..967108407 100644 --- a/pkg/cmd/ssh_alicloud.go +++ b/pkg/cmd/ssh_alicloud.go @@ -118,7 +118,7 @@ func sshToAlicloudNode(nodeName, path, user, pathSSKeypair string, sshPublicKey // fetchAttributes gets all the needed attributes for creating bastion host and its security group with given . func (a *AliyunInstanceAttribute) fetchAttributes(nodeName string) { - a.ShootName = getShootClusterName() + a.ShootName = getTechnicalID() var err error a.InstanceID, err = fetchAlicloudInstanceIDByNodeName(nodeName) checkError(err) @@ -616,7 +616,7 @@ func cleanupAliyunBastionHost() { fmt.Println("") fmt.Println("(2/4) Fetching data from target shoot cluster") - a.ShootName = getShootClusterName() + a.ShootName = getTechnicalID() a.BastionInstanceName = a.ShootName + "-bastion" a.BastionSecurityGroupName = a.ShootName + "-bsg" fmt.Println("Data fetched from target shoot cluster.") diff --git a/pkg/cmd/ssh_aws.go b/pkg/cmd/ssh_aws.go index 73d9abade..64ece72f2 100644 --- a/pkg/cmd/ssh_aws.go +++ b/pkg/cmd/ssh_aws.go @@ -61,11 +61,18 @@ func sshToAWSNode(nodeName, path, user, pathSSKeypair string, sshPublicKey []byt fmt.Println("") fmt.Println("(1/4) Fetching data from target shoot cluster") - a.fetchAwsAttributes(nodeName, path) + + if getRole() == "user" { + a.fetchAwsAttributesByCLI(nodeName, path) + } else { + a.fetchAwsAttributes(nodeName, path) + } + fmt.Println("Data fetched from target shoot cluster.") fmt.Println("") fmt.Println("(2/4) Setting up bastion host security group") + a.createBastionHostSecurityGroup() fmt.Println("") @@ -81,7 +88,7 @@ func sshToAWSNode(nodeName, path, user, pathSSKeypair string, sshPublicKey []byt bastionNode := user + "@" + a.BastionIP node := user + "@" + nodeName - fmt.Print("SSH " + bastionNode + " => " + node) + fmt.Print("SSH " + bastionNode + " => " + node + "\n") key := filepath.Join(pathSSKeypair, "key") sshCmd := fmt.Sprintf("ssh -i " + key + " -o ConnectionAttempts=2 -o \"ProxyCommand ssh -W %%h:%%p -i " + key + " -o IdentitiesOnly=yes -o ConnectionAttempts=2 -o StrictHostKeyChecking=no " + bastionNode + "\" " + node + " -o IdentitiesOnly=yes -o StrictHostKeyChecking=no") @@ -94,9 +101,43 @@ func sshToAWSNode(nodeName, path, user, pathSSKeypair string, sshPublicKey []byt } } +// fetchAwsAttributes gets all the needed attributes for creating bastion host and its security group with given by usering aws cli for non-operator user +func (a *AwsInstanceAttribute) fetchAwsAttributesByCLI(nodeName, path string) { + a.ShootName = getTechnicalID() + publicUtility := a.ShootName + "-public-utility-z0" + arguments := fmt.Sprintf("aws ec2 describe-subnets --filters Name=tag:Name,Values=" + publicUtility + " --query Subnets[*].SubnetId") + captured := capture() + operate("aws", arguments) + capturedOutput, err := captured() + checkError(err) + a.SubnetID = strings.Trim(capturedOutput, "\n") + + arguments = fmt.Sprintf("aws ec2 describe-instances --filters Name=network-interface.private-dns-name,Values=" + nodeName + " --query Reservations[*].Instances[*].{VpcId:VpcId}") + captured = capture() + operate("aws", arguments) + capturedOutput, err = captured() + checkError(err) + a.VpcID = strings.Trim(capturedOutput, "\n") + + a.SecurityGroupName = a.ShootName + "-nodes" + a.getSecurityGroupID() + a.BastionInstanceName = a.ShootName + "-bastions" + a.BastionSecurityGroupName = a.ShootName + "-bsg" + + arguments = fmt.Sprintf("aws ec2 describe-instances --filters Name=network-interface.private-dns-name,Values=" + nodeName + " --query Reservations[*].Instances[*].{ImageId:ImageId}") + captured = capture() + operate("aws", arguments) + capturedOutput, err = captured() + checkError(err) + a.ImageID = strings.Trim(capturedOutput, "\n") + + a.KeyName = a.ShootName + "-ssh-publickey" + a.UserData = getBastionUserData(a.SSHPublicKey) +} + // fetchAwsAttributes gets all the needed attributes for creating bastion host and its security group with given . func (a *AwsInstanceAttribute) fetchAwsAttributes(nodeName, path string) { - a.ShootName = getShootClusterName() + a.ShootName = getTechnicalID() yamlData, err := ioutil.ReadFile(path) checkError(err) diff --git a/pkg/cmd/ssh_azure.go b/pkg/cmd/ssh_azure.go index 8ba5b9bca..7323c4b8c 100644 --- a/pkg/cmd/ssh_azure.go +++ b/pkg/cmd/ssh_azure.go @@ -88,7 +88,7 @@ func sshToAZNode(nodeName, path, user, pathSSKeypair string, sshPublicKey []byte // fetchAttributes gets all the needed attributes for creating bastion host and its security group with given . func (a *AzureInstanceAttribute) fetchAzureAttributes(nodeName, path string) { - a.ShootName = getShootClusterName() + a.ShootName = getTechnicalID() a.NamePublicIP = "sshIP" var err error diff --git a/pkg/cmd/ssh_gcp.go b/pkg/cmd/ssh_gcp.go index 957445afe..493a2b27a 100644 --- a/pkg/cmd/ssh_gcp.go +++ b/pkg/cmd/ssh_gcp.go @@ -85,7 +85,7 @@ func sshToGCPNode(nodeName, path, user, pathSSKeypair string, sshPublicKey []byt // fetchAttributes gets all the needed attributes for creating bastion host and its security group with given . func (g *GCPInstanceAttribute) fetchGCPAttributes(nodeName, path string) { var err error - g.ShootName = getShootClusterName() + g.ShootName = getTechnicalID() g.BastionHostName = g.ShootName + "-bastions" g.FirewallRuleName = g.ShootName + "-allow-ssh-access" g.Subnetwork = g.ShootName + "-nodes" diff --git a/pkg/cmd/target.go b/pkg/cmd/target.go index 863248273..47cedb82c 100644 --- a/pkg/cmd/target.go +++ b/pkg/cmd/target.go @@ -566,7 +566,15 @@ func targetShoot(targetWriter TargetWriter, shoot gardencorev1beta1.Shoot, reade gardenClient, err := target.K8SClientToKind(TargetKindGarden) checkError(err) seedKubeconfigSecret, err := gardenClient.CoreV1().Secrets(seed.Spec.SecretRef.Namespace).Get(seed.Spec.SecretRef.Name, metav1.GetOptions{}) - checkError(err) + // temporary solution , will clean up code in ticket move get seed out of targetShoot method #269 + if err != nil { + if strings.Contains(err.Error(), "forbidden") { + fmt.Printf(warningColor, "\nWarning:\nYou are user role!\n\n") + } else { + checkError(err) + } + } + var seedCacheDir = filepath.Join(pathSeedCache, *shoot.Spec.SeedName) err = os.MkdirAll(seedCacheDir, os.ModePerm) checkError(err) @@ -676,7 +684,15 @@ func getSeedForProject(shootName string) (seedName string) { gardenClientset, err := gardencoreclientset.NewForConfig(NewConfigFromBytes(*kubeconfig)) checkError(err) shootList, err := gardenClientset.CoreV1beta1().Shoots("").List(metav1.ListOptions{}) - checkError(err) + // temporary solution , will clean up code in ticket move get seed out of targetShoot method #269 + if err != nil { + if strings.Contains(err.Error(), "forbidden") { + fmt.Printf(warningColor, "\nWarning:\nYou are user role!\n\n") + } else { + checkError(err) + } + } + for _, item := range shootList.Items { if item.Name == shootName { seedName = *item.Spec.SeedName