1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-08-05 05:45:22 +02:00

resolve conflicts

This commit is contained in:
ArrisLee 2021-09-02 11:33:53 +12:00
commit d191e4f9b9
50 changed files with 950 additions and 182 deletions

View file

@ -45,6 +45,7 @@ func (store *Store) Init() error {
EdgeAgentCheckinInterval: portainer.DefaultEdgeAgentCheckinIntervalInSeconds,
TemplatesURL: portainer.DefaultTemplatesURL,
UserSessionTimeout: portainer.DefaultUserSessionTimeout,
KubeconfigExpiry: portainer.DefaultKubeconfigExpiry,
}
err = store.SettingsService.UpdateSettings(defaultSettings)

View file

@ -24,6 +24,10 @@ func (m *Migrator) migrateDBVersionToDB32() error {
return err
}
if err := m.kubeconfigExpiryToDB32(); err != nil {
return err
}
return nil
}
@ -211,3 +215,12 @@ func findResourcesToUpdateForDB32(dockerID string, volumesData map[string]interf
}
}
}
func (m *Migrator) kubeconfigExpiryToDB32() error {
settings, err := m.settingsService.Settings()
if err != nil {
return err
}
settings.KubeconfigExpiry = portainer.DefaultKubeconfigExpiry
return m.settingsService.UpdateSettings(settings)
}

View file

@ -114,7 +114,7 @@ func initJWTService(dataStore portainer.DataStore) (portainer.JWTService, error)
settings.UserSessionTimeout = portainer.DefaultUserSessionTimeout
dataStore.Settings().UpdateSettings(settings)
}
jwtService, err := jwt.NewService(settings.UserSessionTimeout)
jwtService, err := jwt.NewService(settings.UserSessionTimeout, dataStore)
if err != nil {
return nil, err
}

View file

@ -79,4 +79,4 @@ func (handler *Handler) proxyRequestsToKubernetesAPI(w http.ResponseWriter, r *h
http.StripPrefix(requestPrefix, proxy).ServeHTTP(w, r)
return nil
}
}

View file

@ -69,7 +69,7 @@ type Handler struct {
}
// @title PortainerCE API
// @version 2.1.1
// @version 2.6.3
// @description.markdown api-description.md
// @termsOfService

View file

@ -20,6 +20,7 @@ type Handler struct {
dataStore portainer.DataStore
kubernetesClientFactory *cli.ClientFactory
authorizationService *authorization.Service
JwtService portainer.JWTService
}
// NewHandler creates a handler to process pre-proxied requests to external APIs.
@ -39,6 +40,8 @@ func NewHandler(bouncer *security.RequestBouncer, authorizationService *authoriz
kubeRouter.PathPrefix("/config").Handler(
bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.getKubernetesConfig))).Methods(http.MethodGet)
kubeRouter.PathPrefix("/nodes_limits").Handler(
bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.getKubernetesNodesLimits))).Methods(http.MethodGet)
// namespaces
// in the future this piece of code might be in another package (or a few different packages - namespaces/namespace?)

View file

@ -3,14 +3,11 @@ package kubernetes
import (
"errors"
"fmt"
"strings"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
bolterrors "github.com/portainer/portainer/api/bolt/errors"
httperrors "github.com/portainer/portainer/api/http/errors"
"github.com/portainer/portainer/api/http/security"
kcli "github.com/portainer/portainer/api/kubernetes/cli"
@ -46,16 +43,16 @@ func (handler *Handler) getKubernetesConfig(w http.ResponseWriter, r *http.Reque
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err}
}
bearerToken, err := extractBearerToken(r)
if err != nil {
return &httperror.HandlerError{http.StatusUnauthorized, "Unauthorized", err}
}
tokenData, err := security.RetrieveTokenData(r)
if err != nil {
return &httperror.HandlerError{http.StatusForbidden, "Permission denied to access endpoint", err}
}
bearerToken, err := handler.JwtService.GenerateTokenForKubeconfig(tokenData)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to generate JWT token", err}
}
cli, err := handler.kubernetesClientFactory.GetKubeClient(endpoint)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to create Kubernetes client", err}
@ -84,20 +81,6 @@ func (handler *Handler) getKubernetesConfig(w http.ResponseWriter, r *http.Reque
return response.JSON(w, config)
}
// extractBearerToken extracts user's portainer bearer token from request auth header
func extractBearerToken(r *http.Request) (string, error) {
token := ""
tokens := r.Header["Authorization"]
if len(tokens) >= 1 {
token = tokens[0]
token = strings.TrimPrefix(token, "Bearer ")
}
if token == "" {
return "", httperrors.ErrUnauthorized
}
return token, nil
}
// getProxyUrl generates portainer proxy url which acts as proxy to k8s api server
func getProxyUrl(r *http.Request, endpointID int) string {
return fmt.Sprintf("https://%s/api/endpoints/%d/kubernetes", r.Host, endpointID)

View file

@ -0,0 +1,52 @@
package kubernetes
import (
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
bolterrors "github.com/portainer/portainer/api/bolt/errors"
"net/http"
)
// @id getKubernetesNodesLimits
// @summary Get CPU and memory limits of all nodes within k8s cluster
// @description Get CPU and memory limits of all nodes within k8s cluster
// @description **Access policy**: authorized
// @tags kubernetes
// @security jwt
// @accept json
// @produce json
// @param id path int true "Endpoint identifier"
// @success 200 {object} K8sNodesLimits "Success"
// @failure 400 "Invalid request"
// @failure 401 "Unauthorized"
// @failure 403 "Permission denied"
// @failure 404 "Endpoint not found"
// @failure 500 "Server error"
// @router /kubernetes/{id}/nodes_limits [get]
func (handler *Handler) getKubernetesNodesLimits(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
endpointID, err := request.RetrieveNumericRouteVariableValue(r, "id")
if err != nil {
return &httperror.HandlerError{http.StatusBadRequest, "Invalid endpoint identifier route variable", err}
}
endpoint, err := handler.dataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID))
if err == bolterrors.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err}
} else if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err}
}
cli, err := handler.kubernetesClientFactory.GetKubeClient(endpoint)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to create Kubernetes client", err}
}
nodesLimits, err := cli.GetNodesLimits()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve nodes limits", err}
}
return response.JSON(w, nodesLimits)
}

View file

@ -32,6 +32,8 @@ type settingsUpdatePayload struct {
EnableEdgeComputeFeatures *bool `example:"true"`
// The duration of a user session
UserSessionTimeout *string `example:"5m"`
// The expiry of a Kubeconfig
KubeconfigExpiry *string `example:"24h" default:"0"`
// Whether telemetry is enabled
EnableTelemetry *bool `example:"false"`
}
@ -52,6 +54,12 @@ func (payload *settingsUpdatePayload) Validate(r *http.Request) error {
return errors.New("Invalid user session timeout")
}
}
if payload.KubeconfigExpiry != nil {
_, err := time.ParseDuration(*payload.KubeconfigExpiry)
if err != nil {
return errors.New("Invalid Kubeconfig Expiry")
}
}
return nil
}
@ -135,6 +143,10 @@ func (handler *Handler) settingsUpdate(w http.ResponseWriter, r *http.Request) *
settings.EdgeAgentCheckinInterval = *payload.EdgeAgentCheckinInterval
}
if payload.KubeconfigExpiry != nil {
settings.KubeconfigExpiry = *payload.KubeconfigExpiry
}
if payload.UserSessionTimeout != nil {
settings.UserSessionTimeout = *payload.UserSessionTimeout

View file

@ -103,7 +103,12 @@ func (handler *Handler) createKubernetesStackFromFileContent(w http.ResponseWrit
stackFolder := strconv.Itoa(int(stack.ID))
projectPath, err := handler.FileService.StoreStackFileFromBytes(stackFolder, stack.EntryPoint, []byte(payload.StackFileContent))
if err != nil {
return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Unable to persist Kubernetes manifest file on disk", Err: err}
fileType := "Manifest"
if stack.IsComposeFormat {
fileType = "Compose"
}
errMsg := fmt.Sprintf("Unable to persist Kubernetes %s file on disk", fileType)
return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: errMsg, Err: err}
}
stack.ProjectPath = projectPath

View file

@ -161,6 +161,7 @@ func (server *Server) Start() error {
endpointProxyHandler.ReverseTunnelService = server.ReverseTunnelService
var kubernetesHandler = kubehandler.NewHandler(requestBouncer, server.AuthorizationService, server.DataStore, server.KubernetesClientFactory)
kubernetesHandler.JwtService = server.JWTService
var fileHandler = file.NewHandler(filepath.Join(server.AssetsPath, "public"))

View file

@ -70,6 +70,21 @@ func NewDatastore(options ...datastoreOption) *datastore {
return &d
}
type stubSettingsService struct {
settings *portainer.Settings
}
func (s *stubSettingsService) Settings() (*portainer.Settings, error) { return s.settings, nil }
func (s *stubSettingsService) UpdateSettings(settings *portainer.Settings) error { return nil }
func WithSettings(settings *portainer.Settings) datastoreOption {
return func(d *datastore) {
d.settings = &stubSettingsService{settings: settings}
}
}
type stubUserService struct {
users []portainer.User
}

View file

@ -16,6 +16,7 @@ import (
type Service struct {
secret []byte
userSessionTimeout time.Duration
dataStore portainer.DataStore
}
type claims struct {
@ -31,7 +32,7 @@ var (
)
// NewService initializes a new service. It will generate a random key that will be used to sign JWT tokens.
func NewService(userSessionDuration string) (*Service, error) {
func NewService(userSessionDuration string, dataStore portainer.DataStore) (*Service, error) {
userSessionTimeout, err := time.ParseDuration(userSessionDuration)
if err != nil {
return nil, err
@ -45,19 +46,28 @@ func NewService(userSessionDuration string) (*Service, error) {
service := &Service{
secret,
userSessionTimeout,
dataStore,
}
return service, nil
}
func (service *Service) defaultExpireAt() (int64) {
return time.Now().Add(service.userSessionTimeout).Unix()
}
// GenerateToken generates a new JWT token.
func (service *Service) GenerateToken(data *portainer.TokenData) (string, error) {
return service.generateSignedToken(data, nil)
return service.generateSignedToken(data, service.defaultExpireAt())
}
// GenerateTokenForOAuth generates a new JWT for OAuth login
// token expiry time from the OAuth provider is considered
func (service *Service) GenerateTokenForOAuth(data *portainer.TokenData, expiryTime *time.Time) (string, error) {
return service.generateSignedToken(data, expiryTime)
expireAt := service.defaultExpireAt()
if expiryTime != nil && !expiryTime.IsZero() {
expireAt = expiryTime.Unix()
}
return service.generateSignedToken(data, expireAt)
}
// ParseAndVerifyToken parses a JWT token and verify its validity. It returns an error if token is invalid.
@ -88,17 +98,13 @@ func (service *Service) SetUserSessionDuration(userSessionDuration time.Duration
service.userSessionTimeout = userSessionDuration
}
func (service *Service) generateSignedToken(data *portainer.TokenData, expiryTime *time.Time) (string, error) {
expireToken := time.Now().Add(service.userSessionTimeout).Unix()
if expiryTime != nil && !expiryTime.IsZero() {
expireToken = expiryTime.Unix()
}
func (service *Service) generateSignedToken(data *portainer.TokenData, expiresAt int64) (string, error) {
cl := claims{
UserID: int(data.ID),
Username: data.Username,
Role: int(data.Role),
StandardClaims: jwt.StandardClaims{
ExpiresAt: expireToken,
ExpiresAt: expiresAt,
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, cl)

26
api/jwt/jwt_kubeconfig.go Normal file
View file

@ -0,0 +1,26 @@
package jwt
import (
portainer "github.com/portainer/portainer/api"
"time"
)
// GenerateTokenForKubeconfig generates a new JWT token for Kubeconfig
func (service *Service) GenerateTokenForKubeconfig(data *portainer.TokenData) (string, error) {
settings, err := service.dataStore.Settings().Settings()
if err != nil {
return "", err
}
expiryDuration, err := time.ParseDuration(settings.KubeconfigExpiry)
if err != nil {
return "", err
}
expiryAt := time.Now().Add(expiryDuration).Unix()
if expiryDuration == time.Duration(0) {
expiryAt = 0
}
return service.generateSignedToken(data, expiryAt)
}

View file

@ -0,0 +1,81 @@
package jwt
import (
"github.com/dgrijalva/jwt-go"
portainer "github.com/portainer/portainer/api"
i "github.com/portainer/portainer/api/internal/testhelpers"
"github.com/stretchr/testify/assert"
"testing"
)
func TestService_GenerateTokenForKubeconfig(t *testing.T) {
type fields struct {
userSessionTimeout string
dataStore portainer.DataStore
}
type args struct {
data *portainer.TokenData
}
mySettings := &portainer.Settings{
KubeconfigExpiry: "0",
}
myFields := fields{
userSessionTimeout: "24h",
dataStore: i.NewDatastore(i.WithSettings(mySettings)),
}
myTokenData := &portainer.TokenData{
Username: "Joe",
ID: 1,
Role: 1,
}
myArgs := args{
data: myTokenData,
}
tests := []struct {
name string
fields fields
args args
wantExpiresAt int64
wantErr bool
}{
{
name: "kubeconfig no expiry",
fields: myFields,
args: myArgs,
wantExpiresAt: 0,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
service, err := NewService(tt.fields.userSessionTimeout, tt.fields.dataStore)
assert.NoError(t, err, "failed to create a copy of service")
got, err := service.GenerateTokenForKubeconfig(tt.args.data)
if (err != nil) != tt.wantErr {
t.Errorf("GenerateTokenForKubeconfig() error = %v, wantErr %v", err, tt.wantErr)
return
}
parsedToken, err := jwt.ParseWithClaims(got, &claims{}, func(token *jwt.Token) (interface{}, error) {
return service.secret, nil
})
assert.NoError(t, err, "failed to parse generated token")
tokenClaims, ok := parsedToken.Claims.(*claims)
assert.Equal(t, true, ok, "failed to claims out of generated ticket")
assert.Equal(t, myTokenData.Username, tokenClaims.Username)
assert.Equal(t, int(myTokenData.ID), tokenClaims.UserID)
assert.Equal(t, int(myTokenData.Role), tokenClaims.Role)
assert.Equal(t, tt.wantExpiresAt, tokenClaims.ExpiresAt)
})
}
}

View file

@ -10,7 +10,7 @@ import (
)
func TestGenerateSignedToken(t *testing.T) {
svc, err := NewService("24h")
svc, err := NewService("24h", nil)
assert.NoError(t, err, "failed to create a copy of service")
token := &portainer.TokenData{
@ -18,9 +18,9 @@ func TestGenerateSignedToken(t *testing.T) {
ID: 1,
Role: 1,
}
expirtationTime := time.Now().Add(1 * time.Hour)
expiresAt := time.Now().Add(1 * time.Hour).Unix()
generatedToken, err := svc.generateSignedToken(token, &expirtationTime)
generatedToken, err := svc.generateSignedToken(token, expiresAt)
assert.NoError(t, err, "failed to generate a signed token")
parsedToken, err := jwt.ParseWithClaims(generatedToken, &claims{}, func(token *jwt.Token) (interface{}, error) {
@ -34,5 +34,5 @@ func TestGenerateSignedToken(t *testing.T) {
assert.Equal(t, token.Username, tokenClaims.Username)
assert.Equal(t, int(token.ID), tokenClaims.UserID)
assert.Equal(t, int(token.Role), tokenClaims.Role)
assert.Equal(t, expirtationTime.Unix(), tokenClaims.ExpiresAt)
assert.Equal(t, expiresAt, tokenClaims.ExpiresAt)
}

View file

@ -0,0 +1,42 @@
package cli
import (
portainer "github.com/portainer/portainer/api"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// GetNodesLimits gets the CPU and Memory limits(unused resources) of all nodes in the current k8s endpoint connection
func (kcl *KubeClient) GetNodesLimits() (portainer.K8sNodesLimits, error) {
nodesLimits := make(portainer.K8sNodesLimits)
nodes, err := kcl.cli.CoreV1().Nodes().List(metav1.ListOptions{})
if err != nil {
return nil, err
}
pods, err := kcl.cli.CoreV1().Pods("").List(metav1.ListOptions{})
if err != nil {
return nil, err
}
for _, item := range nodes.Items {
cpu := item.Status.Allocatable.Cpu().MilliValue()
memory := item.Status.Allocatable.Memory().Value()
nodesLimits[item.ObjectMeta.Name] = &portainer.K8sNodeLimits{
CPU: cpu,
Memory: memory,
}
}
for _, item := range pods.Items {
if nodeLimits, ok := nodesLimits[item.Spec.NodeName]; ok {
for _, container := range item.Spec.Containers {
nodeLimits.CPU -= container.Resources.Requests.Cpu().MilliValue()
nodeLimits.Memory -= container.Resources.Requests.Memory().Value()
}
}
}
return nodesLimits, nil
}

View file

@ -0,0 +1,137 @@
package cli
import (
portainer "github.com/portainer/portainer/api"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
kfake "k8s.io/client-go/kubernetes/fake"
"reflect"
"testing"
)
func newNodes() *v1.NodeList {
return &v1.NodeList{
Items: []v1.Node{
{
ObjectMeta: metav1.ObjectMeta{
Name: "test-node-0",
},
Status: v1.NodeStatus{
Allocatable: v1.ResourceList{
v1.ResourceName(v1.ResourceCPU): resource.MustParse("2"),
v1.ResourceName(v1.ResourceMemory): resource.MustParse("4M"),
},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "test-node-1",
},
Status: v1.NodeStatus{
Allocatable: v1.ResourceList{
v1.ResourceName(v1.ResourceCPU): resource.MustParse("3"),
v1.ResourceName(v1.ResourceMemory): resource.MustParse("6M"),
},
},
},
},
}
}
func newPods() *v1.PodList {
return &v1.PodList{
Items: []v1.Pod{
{
ObjectMeta: metav1.ObjectMeta{
Name: "test-container-0",
Namespace: "test-namespace-0",
},
Spec: v1.PodSpec{
NodeName: "test-node-0",
Containers: []v1.Container{
{
Name: "test-container-0",
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceName(v1.ResourceCPU): resource.MustParse("1"),
v1.ResourceName(v1.ResourceMemory): resource.MustParse("2M"),
},
},
},
},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "test-container-1",
Namespace: "test-namespace-1",
},
Spec: v1.PodSpec{
NodeName: "test-node-1",
Containers: []v1.Container{
{
Name: "test-container-1",
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceName(v1.ResourceCPU): resource.MustParse("2"),
v1.ResourceName(v1.ResourceMemory): resource.MustParse("3M"),
},
},
},
},
},
},
},
}
}
func TestKubeClient_GetNodesLimits(t *testing.T) {
type fields struct {
cli kubernetes.Interface
}
fieldsInstance := fields{
cli: kfake.NewSimpleClientset(newNodes(), newPods()),
}
tests := []struct {
name string
fields fields
want portainer.K8sNodesLimits
wantErr bool
}{
{
name: "2 nodes 2 pods",
fields: fieldsInstance,
want: portainer.K8sNodesLimits{
"test-node-0": &portainer.K8sNodeLimits{
CPU: 1000,
Memory: 2000000,
},
"test-node-1": &portainer.K8sNodeLimits{
CPU: 1000,
Memory: 3000000,
},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
kcl := &KubeClient{
cli: tt.fields.cli,
}
got, err := kcl.GetNodesLimits()
if (err != nil) != tt.wantErr {
t.Errorf("GetNodesLimits() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("GetNodesLimits() got = %v, want %v", got, tt.want)
}
})
}
}

View file

@ -18,15 +18,10 @@ func getPortainerUserDefaultPolicies() []rbacv1.PolicyRule {
Resources: []string{"storageclasses"},
APIGroups: []string{"storage.k8s.io"},
},
{
Verbs: []string{"list"},
Resources: []string{"ingresses"},
APIGroups: []string{"networking.k8s.io"},
},
}
}
func (kcl *KubeClient) createPortainerUserClusterRole() error {
func (kcl *KubeClient) upsertPortainerK8sClusterRoles() error {
clusterRole := &rbacv1.ClusterRole{
ObjectMeta: metav1.ObjectMeta{
Name: portainerUserCRName,
@ -35,8 +30,13 @@ func (kcl *KubeClient) createPortainerUserClusterRole() error {
}
_, err := kcl.cli.RbacV1().ClusterRoles().Create(clusterRole)
if err != nil && !k8serrors.IsAlreadyExists(err) {
return err
if err != nil {
if k8serrors.IsAlreadyExists(err) {
_, err = kcl.cli.RbacV1().ClusterRoles().Update(clusterRole)
}
if err != nil {
return err
}
}
return nil

View file

@ -63,7 +63,7 @@ func (kcl *KubeClient) SetupUserServiceAccount(userID int, teamIDs []int, restri
}
func (kcl *KubeClient) ensureRequiredResourcesExist() error {
return kcl.createPortainerUserClusterRole()
return kcl.upsertPortainerK8sClusterRoles()
}
func (kcl *KubeClient) createUserServiceAccount(namespace, serviceAccountName string) error {

View file

@ -398,6 +398,13 @@ type (
// JobType represents a job type
JobType int
K8sNodeLimits struct {
CPU int64 `json:"CPU"`
Memory int64 `json:"Memory"`
}
K8sNodesLimits map[string]*K8sNodeLimits
K8sNamespaceAccessPolicy struct {
UserAccessPolicies UserAccessPolicies `json:"UserAccessPolicies"`
TeamAccessPolicies TeamAccessPolicies `json:"TeamAccessPolicies"`
@ -682,6 +689,8 @@ type (
EnableEdgeComputeFeatures bool `json:"EnableEdgeComputeFeatures" example:""`
// The duration of a user session
UserSessionTimeout string `json:"UserSessionTimeout" example:"5m"`
// The expiry of a Kubeconfig
KubeconfigExpiry string `json:"KubeconfigExpiry" example:"24h"`
// Whether telemetry is enabled
EnableTelemetry bool `json:"EnableTelemetry" example:"false"`
@ -1212,18 +1221,20 @@ type (
JWTService interface {
GenerateToken(data *TokenData) (string, error)
GenerateTokenForOAuth(data *TokenData, expiryTime *time.Time) (string, error)
GenerateTokenForKubeconfig(data *TokenData) (string, error)
ParseAndVerifyToken(token string) (*TokenData, error)
SetUserSessionDuration(userSessionDuration time.Duration)
}
// KubeClient represents a service used to query a Kubernetes environment
KubeClient interface {
GetServiceAccount(tokendata *TokenData) (*v1.ServiceAccount, error)
SetupUserServiceAccount(userID int, teamIDs []int, restrictDefaultNamespace bool) error
GetServiceAccount(tokendata *TokenData) (*v1.ServiceAccount, error)
GetServiceAccountBearerToken(userID int) (string, error)
CreateUserShellPod(ctx context.Context, serviceAccountName string) (*KubernetesShellPod, error)
StartExecProcess(token string, useAdminToken bool, namespace, podName, containerName string, command []string, stdin io.Reader, stdout io.Writer) error
NamespaceAccessPoliciesDeleteNamespace(namespace string) error
GetNodesLimits() (K8sNodesLimits, error)
GetNamespaceAccessPolicies() (map[string]K8sNamespaceAccessPolicy, error)
UpdateNamespaceAccessPolicies(accessPolicies map[string]K8sNamespaceAccessPolicy) error
DeleteRegistrySecret(registry *Registry, namespace string) error
@ -1415,7 +1426,7 @@ type (
const (
// APIVersion is the version number of the Portainer API
APIVersion = "2.6.2"
APIVersion = "2.6.3"
// DBVersion is the version number of the Portainer database
DBVersion = 32
// ComposeSyntaxMaxVersion is a maximum supported version of the docker compose syntax
@ -1449,6 +1460,8 @@ const (
DefaultTemplatesURL = "https://raw.githubusercontent.com/portainer/templates/master/templates-2.0.json"
// DefaultUserSessionTimeout represents the default timeout after which the user session is cleared
DefaultUserSessionTimeout = "8h"
// DefaultUserSessionTimeout represents the default timeout after which the user session is cleared
DefaultKubeconfigExpiry = "0"
)
const (