mirror of
https://github.com/portainer/portainer.git
synced 2025-07-24 15:59:41 +02:00
feat(app): rework private registries and support private registries in kubernetes EE-30 (#5131)
* feat(app): rework private registries and support private registries in kubernetes [EE-30] feat(api): backport private registries backend changes (#5072) * feat(api/bolt): backport bolt changes * feat(api/exec): backport exec changes * feat(api/http): backport http/handler/dockerhub changes * feat(api/http): backport http/handler/endpoints changes * feat(api/http): backport http/handler/registries changes * feat(api/http): backport http/handler/stacks changes * feat(api/http): backport http/handler changes * feat(api/http): backport http/proxy/factory/azure changes * feat(api/http): backport http/proxy/factory/docker changes * feat(api/http): backport http/proxy/factory/utils changes * feat(api/http): backport http/proxy/factory/kubernetes changes * feat(api/http): backport http/proxy/factory changes * feat(api/http): backport http/security changes * feat(api/http): backport http changes * feat(api/internal): backport internal changes * feat(api): backport api changes * feat(api/kubernetes): backport kubernetes changes * fix(api/http): changes on backend following backport feat(app): backport private registries frontend changes (#5056) * feat(app/docker): backport docker/components changes * feat(app/docker): backport docker/helpers changes * feat(app/docker): backport docker/views/container changes * feat(app/docker): backport docker/views/images changes * feat(app/docker): backport docker/views/registries changes * feat(app/docker): backport docker/views/services changes * feat(app/docker): backport docker changes * feat(app/kubernetes): backport kubernetes/components changes * feat(app/kubernetes): backport kubernetes/converters changes * feat(app/kubernetes): backport kubernetes/models changes * feat(app/kubernetes): backport kubernetes/registries changes * feat(app/kubernetes): backport kubernetes/services changes * feat(app/kubernetes): backport kubernetes/views/applications changes * feat(app/kubernetes): backport kubernetes/views/configurations changes * feat(app/kubernetes): backport kubernetes/views/configure changes * feat(app/kubernetes): backport kubernetes/views/resource-pools changes * feat(app/kubernetes): backport kubernetes/views changes * feat(app/portainer): backport portainer/components/accessManagement changes * feat(app/portainer): backport portainer/components/datatables changes * feat(app/portainer): backport portainer/components/forms changes * feat(app/portainer): backport portainer/components/registry-details changes * feat(app/portainer): backport portainer/models changes * feat(app/portainer): backport portainer/rest changes * feat(app/portainer): backport portainer/services changes * feat(app/portainer): backport portainer/views changes * feat(app/portainer): backport portainer changes * feat(app): backport app changes * config(project): gitignore + jsconfig changes gitignore all files under api/cmd/portainer but main.go and enable Code Editor autocomplete on import ... from '@/...' fix(app): fix pull rate limit checker fix(app/registries): sidebar menus and registry accesses users filtering fix(api): add missing kube client factory fix(kube): fetch dockerhub pull limits (#5133) fix(app): pre review fixes (#5142) * fix(app/registries): remove checkbox for endpointRegistries view * fix(endpoints): allow access to default namespace * fix(docker): fetch pull limits * fix(kube/ns): show selected registries for non admin Co-authored-by: Chaim Lev-Ari <chiptus@gmail.com> chore(webpack): ignore missing sourcemaps fix(registries): fetch registry config from url feat(kube/registries): ignore not found when deleting secret feat(db): move migration to db 31 fix(registries): fix bugs in PR EE-869 (#5169) * fix(registries): hide role * fix(endpoints): set empty access policy to edge endpoint * fix(registry): remove double arguments * fix(admin): ignore warning * feat(kube/configurations): tag registry secrets (#5157) * feat(kube/configurations): tag registry secrets * feat(kube/secrets): show registry secrets for admins * fix(registries): move dockerhub to beginning * refactor(registries): use endpoint scoped registries feat(registries): filter by namespace if supplied feat(access-managment): filter users for registry (#5191) * refactor(access-manage): move users selector to component * feat(access-managment): filter users for registry refactor(registries): sync code with CE (#5200) * refactor(registry): add inspect handler under endpoints * refactor(endpoint): sync endpoint_registries_list * refactor(endpoints): sync registry_access * fix(db): rename migration functions * fix(registries): show accesses for admin * fix(kube): set token on transport * refactor(kube): move secret help to bottom * fix(kuberentes): remove shouldLog parameter * style(auth): add description of security.IsAdmin * feat(security): allow admin access to registry * feat(edge): connect to edge endpoint when creating client * style(portainer): change deprecation version * refactor(sidebar): hide manage * refactor(containers): revert changes * style(container): remove whitespace * fix(endpoint): add handler to registy on endpointService * refactor(image): use endpointService.registries * fix(kueb/namespaces): rename resource pool to namespace * fix(kube/namespace): move selected registries * fix(api/registries): hide accesses on registry creation Co-authored-by: LP B <xAt0mZ@users.noreply.github.com> refactor(api): remove code duplication after rebase fix(app/registries): replace last registry api usage by endpoint registry api fix(api/endpoints): update registry access policies on endpoint deletion (#5226) [EE-1027] fix(db): update db version * fix(dockerhub): fetch rate limits * fix(registry/tests): supply restricred context * fix(registries): show proget registry only when selected * fix(registry): create dockerhub registry * feat(db): move migrations to db 32 Co-authored-by: Chaim Lev-Ari <chiptus@gmail.com>
This commit is contained in:
parent
0f5407da40
commit
179df06267
175 changed files with 3757 additions and 2544 deletions
|
@ -12,6 +12,28 @@ type (
|
|||
namespaceAccessPolicies map[string]portainer.K8sNamespaceAccessPolicy
|
||||
)
|
||||
|
||||
// GetNamespaceAccessPolicies gets the namespace access policies
|
||||
// from config maps in the portainer namespace
|
||||
func (kcl *KubeClient) GetNamespaceAccessPolicies() (
|
||||
map[string]portainer.K8sNamespaceAccessPolicy, error,
|
||||
) {
|
||||
configMap, err := kcl.cli.CoreV1().ConfigMaps(portainerNamespace).Get(portainerConfigMapName, metav1.GetOptions{})
|
||||
if k8serrors.IsNotFound(err) {
|
||||
return nil, nil
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
accessData := configMap.Data[portainerConfigMapAccessPoliciesKey]
|
||||
|
||||
var policies map[string]portainer.K8sNamespaceAccessPolicy
|
||||
err = json.Unmarshal([]byte(accessData), &policies)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return policies, nil
|
||||
}
|
||||
|
||||
func (kcl *KubeClient) setupNamespaceAccesses(userID int, teamIDs []int, serviceAccountName string) error {
|
||||
configMap, err := kcl.cli.CoreV1().ConfigMaps(portainerNamespace).Get(portainerConfigMapName, metav1.GetOptions{})
|
||||
if k8serrors.IsNotFound(err) {
|
||||
|
@ -80,28 +102,6 @@ func hasUserAccessToNamespace(userID int, teamIDs []int, policies portainer.K8sN
|
|||
return false
|
||||
}
|
||||
|
||||
// GetNamespaceAccessPolicies gets the namespace access policies
|
||||
// from config maps in the portainer namespace
|
||||
func (kcl *KubeClient) GetNamespaceAccessPolicies() (map[string]portainer.K8sNamespaceAccessPolicy, error) {
|
||||
configMap, err := kcl.cli.CoreV1().ConfigMaps(portainerNamespace).Get(portainerConfigMapName, metav1.GetOptions{})
|
||||
if k8serrors.IsNotFound(err) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
accessData := configMap.Data[portainerConfigMapAccessPoliciesKey]
|
||||
|
||||
var policies map[string]portainer.K8sNamespaceAccessPolicy
|
||||
err = json.Unmarshal([]byte(accessData), &policies)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return policies, nil
|
||||
}
|
||||
|
||||
// UpdateNamespaceAccessPolicies updates the namespace access policies
|
||||
func (kcl *KubeClient) UpdateNamespaceAccessPolicies(accessPolicies map[string]portainer.K8sNamespaceAccessPolicy) error {
|
||||
data, err := json.Marshal(accessPolicies)
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
cmap "github.com/orcaman/concurrent-map"
|
||||
|
||||
|
@ -17,6 +18,7 @@ import (
|
|||
type (
|
||||
// ClientFactory is used to create Kubernetes clients
|
||||
ClientFactory struct {
|
||||
dataStore portainer.DataStore
|
||||
reverseTunnelService portainer.ReverseTunnelService
|
||||
signatureService portainer.DigitalSignatureService
|
||||
instanceID string
|
||||
|
@ -31,8 +33,9 @@ type (
|
|||
)
|
||||
|
||||
// NewClientFactory returns a new instance of a ClientFactory
|
||||
func NewClientFactory(signatureService portainer.DigitalSignatureService, reverseTunnelService portainer.ReverseTunnelService, instanceID string) *ClientFactory {
|
||||
func NewClientFactory(signatureService portainer.DigitalSignatureService, reverseTunnelService portainer.ReverseTunnelService, instanceID string, dataStore portainer.DataStore) *ClientFactory {
|
||||
return &ClientFactory{
|
||||
dataStore: dataStore,
|
||||
signatureService: signatureService,
|
||||
reverseTunnelService: reverseTunnelService,
|
||||
instanceID: instanceID,
|
||||
|
@ -133,7 +136,29 @@ func (factory *ClientFactory) buildAgentClient(endpoint *portainer.Endpoint) (*k
|
|||
|
||||
func (factory *ClientFactory) buildEdgeClient(endpoint *portainer.Endpoint) (*kubernetes.Clientset, error) {
|
||||
tunnel := factory.reverseTunnelService.GetTunnelDetails(endpoint.ID)
|
||||
endpointURL := fmt.Sprintf("http://localhost:%d/kubernetes", tunnel.Port)
|
||||
|
||||
if tunnel.Status == portainer.EdgeAgentIdle {
|
||||
err := factory.reverseTunnelService.SetTunnelStatusToRequired(endpoint.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed opening tunnel to endpoint: %w", err)
|
||||
}
|
||||
|
||||
if endpoint.EdgeCheckinInterval == 0 {
|
||||
settings, err := factory.dataStore.Settings().Settings()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed fetching settings from db: %w", err)
|
||||
}
|
||||
|
||||
endpoint.EdgeCheckinInterval = settings.EdgeAgentCheckinInterval
|
||||
}
|
||||
|
||||
waitForAgentToConnect := time.Duration(endpoint.EdgeCheckinInterval) * time.Second
|
||||
time.Sleep(waitForAgentToConnect * 2)
|
||||
|
||||
tunnel = factory.reverseTunnelService.GetTunnelDetails(endpoint.ID)
|
||||
}
|
||||
|
||||
endpointURL := fmt.Sprintf("http://127.0.0.1:%d/kubernetes", tunnel.Port)
|
||||
|
||||
config, err := clientcmd.BuildConfigFromFlags(endpointURL, "")
|
||||
if err != nil {
|
||||
|
|
96
api/kubernetes/cli/registries.go
Normal file
96
api/kubernetes/cli/registries.go
Normal file
|
@ -0,0 +1,96 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
secretDockerConfigKey = ".dockerconfigjson"
|
||||
)
|
||||
|
||||
type (
|
||||
dockerConfig struct {
|
||||
Auths map[string]registryDockerConfig `json:"auths"`
|
||||
}
|
||||
|
||||
registryDockerConfig struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
)
|
||||
|
||||
func (kcl *KubeClient) DeleteRegistrySecret(registry *portainer.Registry, namespace string) error {
|
||||
err := kcl.cli.CoreV1().Secrets(namespace).Delete(registrySecretName(registry), &metav1.DeleteOptions{})
|
||||
if err != nil && !k8serrors.IsNotFound(err) {
|
||||
return errors.Wrap(err, "failed removing secret")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (kcl *KubeClient) CreateRegistrySecret(registry *portainer.Registry, namespace string) error {
|
||||
config := dockerConfig{
|
||||
Auths: map[string]registryDockerConfig{
|
||||
registry.URL: {
|
||||
Username: registry.Username,
|
||||
Password: registry.Password,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
configByte, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed marshal config")
|
||||
}
|
||||
|
||||
secret := &v1.Secret{
|
||||
TypeMeta: metav1.TypeMeta{},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: registrySecretName(registry),
|
||||
Annotations: map[string]string{
|
||||
"portainer.io/registry.id": strconv.Itoa(int(registry.ID)),
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
secretDockerConfigKey: configByte,
|
||||
},
|
||||
Type: v1.SecretTypeDockerConfigJson,
|
||||
}
|
||||
|
||||
_, err = kcl.cli.CoreV1().Secrets(namespace).Create(secret)
|
||||
if err != nil && !k8serrors.IsAlreadyExists(err) {
|
||||
return errors.Wrap(err, "failed saving secret")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (cli *KubeClient) IsRegistrySecret(namespace, secretName string) (bool, error) {
|
||||
secret, err := cli.cli.CoreV1().Secrets(namespace).Get(secretName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
if k8serrors.IsNotFound(err) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return false, err
|
||||
}
|
||||
|
||||
isSecret := secret.Type == v1.SecretTypeDockerConfigJson
|
||||
|
||||
return isSecret, nil
|
||||
|
||||
}
|
||||
|
||||
func registrySecretName(registry *portainer.Registry) string {
|
||||
return fmt.Sprintf("registry-%d", registry.ID)
|
||||
}
|
5
api/kubernetes/privateregistries/labels.go
Normal file
5
api/kubernetes/privateregistries/labels.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
package privateregistries
|
||||
|
||||
const (
|
||||
RegistryIDLabel = "portainer.io/registry.id"
|
||||
)
|
Loading…
Add table
Add a link
Reference in a new issue