-
Notifications
You must be signed in to change notification settings - Fork 1
/
pod.go
119 lines (105 loc) · 4.13 KB
/
pod.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
package pod
import (
"encoding/base64"
"fmt"
"net/http"
"strings"
"github.com/rancher/rancher/pkg/kubectl"
"github.com/mitchellh/mapstructure"
"github.com/rancher/norman/api/access"
"github.com/rancher/norman/api/handler"
"github.com/rancher/norman/httperror"
"github.com/rancher/norman/types"
"github.com/rancher/rancher/pkg/clustermanager"
"github.com/rancher/types/apis/project.cattle.io/v3/schema"
projectschema "github.com/rancher/types/apis/project.cattle.io/v3/schema"
projectclient "github.com/rancher/types/client/project/v3"
"github.com/rancher/types/config"
"github.com/rancher/types/user"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)
type ActionWrapper struct {
ClusterManager *clustermanager.Manager
UserManager user.Manager
}
func (a ActionWrapper) ActionHandler(actionName string, action *types.Action, apiContext *types.APIContext) error {
var pod projectclient.Pod
accessError := access.ByID(apiContext, &projectschema.Version, "pod", apiContext.ID, &pod)
if accessError != nil {
return httperror.NewAPIError(httperror.InvalidReference, "Error accessing pod")
}
namespace, name := splitID(pod.ID)
switch actionName {
case "download":
clusterName := a.ClusterManager.ClusterName(apiContext)
if clusterName == "" {
return httperror.NewAPIError(httperror.ServerError, fmt.Sprintf("Cluster name empty %s", pod.ID))
}
clusterContext, err := a.ClusterManager.UserContext(clusterName)
if err != nil {
return httperror.NewAPIError(httperror.ServerError, fmt.Sprintf("Error getting cluster context %s", pod.ID))
}
return a.downloadPodFile(apiContext, clusterContext, actionName, pod, namespace, name)
}
return nil
}
func Formatter(apiContext *types.APIContext, resource *types.RawResource) {
podID := resource.ID
podSchema := apiContext.Schemas.Schema(&schema.Version, "pod")
resource.Actions["download"] = apiContext.URLBuilder.ActionLinkByID(podSchema, podID, "download")
}
func splitID(id string) (string, string) {
namespace := ""
parts := strings.SplitN(id, ":", 2)
if len(parts) == 2 {
namespace = parts[0]
id = parts[1]
}
return namespace, id
}
func (a ActionWrapper) getToken(apiContext *types.APIContext) (string, error) {
userName := a.UserManager.GetUser(apiContext)
return a.UserManager.EnsureToken("kubeconfig-"+userName, "Kubeconfig token", userName)
}
func (a ActionWrapper) getKubeConfig(apiContext *types.APIContext, clusterContext *config.UserContext) (*clientcmdapi.Config, error) {
token, err := a.getToken(apiContext)
if err != nil {
return nil, err
}
cfg := a.ClusterManager.KubeConfig(clusterContext.ClusterName, token)
return cfg, nil
}
func (a ActionWrapper) downloadPodFile(apiContext *types.APIContext, clusterContext *config.UserContext,
actionName string, pod projectclient.Pod, namespace string, name string) error {
input, err := handler.ParseAndValidateActionBody(apiContext, apiContext.Schemas.Schema(&projectschema.Version,
projectclient.PodFileDownloadInputType))
if err != nil {
return httperror.NewAPIError(httperror.InvalidBodyContent,
fmt.Sprintf("Failed to parse action body: %v", err))
}
podFileDownloadInput := &projectclient.PodFileDownloadInput{}
if err := mapstructure.Decode(input, podFileDownloadInput); err != nil {
return httperror.NewAPIError(httperror.InvalidBodyContent,
fmt.Sprintf("Failed to parse body: %v", err))
}
cfg, err := a.getKubeConfig(apiContext, clusterContext)
if err != nil {
return httperror.NewAPIError(httperror.InvalidState,
fmt.Sprintf("Failed to get kubeconfig: %v", err))
}
if podFileDownloadInput.ContainerName == "" &&
len(pod.Containers) > 0 {
podFileDownloadInput.ContainerName = pod.Containers[0].Name
}
b, err := kubectl.Copy(namespace, name, podFileDownloadInput.ContainerName, podFileDownloadInput.FilePath, cfg)
if err != nil {
return httperror.NewAPIError(httperror.ServerError,
fmt.Sprintf("Failed to copy %s/%s:%s - %v", namespace, name, podFileDownloadInput.FilePath, err))
}
data := map[string]interface{}{
"type": "podFileDownloadOutput",
projectclient.PodFileDownloadOutputFieldFileContent: base64.StdEncoding.EncodeToString(b),
}
apiContext.WriteResponse(http.StatusOK, data)
return nil
}