Skip to content

Commit

Permalink
internal/profile: move FromKubeconfig here to be used from other comm…
Browse files Browse the repository at this point in the history
…ands

Signed-off-by: Dr. Stefan Schimanski <stefan.schimanski@upbound.com>
  • Loading branch information
sttts authored and Piotr1215 committed Apr 4, 2024
1 parent 5b3641f commit da4642b
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 130 deletions.
109 changes: 1 addition & 108 deletions cmd/up/ctx/profiles.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,8 @@ package ctx

import (
"context"
"fmt"
"net/url"
"strings"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/upbound/up/internal/profile"
"github.com/upbound/up/internal/upbound"
Expand All @@ -37,7 +29,7 @@ func DeriveState(ctx context.Context, upCtx *upbound.Context, conf *clientcmdapi
if err != nil {
return nil, err
}
name, p, ctp, err := findProfileByURL(ctx, profiles, conf, getIngressHost)
name, p, ctp, err := profile.FromKubeconfig(ctx, profiles, conf)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -74,102 +66,3 @@ func DeriveState(ctx context.Context, upCtx *upbound.Context, conf *clientcmdapi
}, nil
}
}

func findProfileByURL(ctx context.Context, profiles map[string]profile.Profile, conf *clientcmdapi.Config, getIngressHost func(ctx context.Context, cfg *rest.Config) (string, error)) (string, *profile.Profile, types.NamespacedName, error) { // nolint:gocyclo // TODO: shorten
// get base URL of Upbound profiles
profileURLs := make(map[string]string)
for name, p := range profiles {
if !p.IsSpace() {
continue
}
confCtx, ok := conf.Contexts[p.KubeContext]
if !ok {
continue
}
cluster, ok := conf.Clusters[confCtx.Cluster]
if !ok {
continue
}
profileURLs[name] = cluster.Server
}

// get user cluster URL
confCtx, ok := conf.Contexts[conf.CurrentContext]
if !ok {
return "", nil, types.NamespacedName{}, fmt.Errorf("current context %q not found in kubeconfig", conf.CurrentContext)
}
cluster, ok := conf.Clusters[confCtx.Cluster]
if !ok {
return "", nil, types.NamespacedName{}, fmt.Errorf("cluster %q not found in kubeconfig", confCtx.Cluster)
}

// find profile by URL
for name, profileURL := range profileURLs {
if strings.TrimSuffix(profileURL, "/") == strings.TrimSuffix(cluster.Server, "/") {
p := profiles[name]
ns := confCtx.Namespace
if ns == "" {
ns = corev1.NamespaceDefault
}
return name, &p, types.NamespacedName{Namespace: ns}, nil
}
url, err := url.Parse(profileURL)
if err != nil {
continue
}

// profile points to Spaces API?
if strings.HasPrefix(cluster.Server, strings.TrimSuffix(url.String(), "/")+"/") {
p := profiles[name]

ctp, found := profile.ParseSpacesK8sURL(strings.TrimSuffix(cluster.Server, "/"))
if !found {
return "", nil, types.NamespacedName{}, fmt.Errorf("not connected to a control plane or Space")
}

return name, &p, ctp, nil
}
}

// still not found. Try ingresses.
for name, p := range profiles {
if !p.IsSpace() {
continue
}
pconf := conf.DeepCopy()
pconf.CurrentContext = p.KubeContext
cfg, err := clientcmd.NewDefaultClientConfig(*pconf, &clientcmd.ConfigOverrides{}).ClientConfig()
if err != nil {
continue
}
ingressHost, err := getIngressHost(ctx, cfg)
if err != nil {
continue
}
if !strings.HasPrefix(ingressHost, "https://") {
ingressHost = "https://" + ingressHost
}
if strings.HasPrefix(cluster.Server, strings.TrimSuffix(ingressHost, "/")+"/") {
ctp, found := profile.ParseSpacesK8sURL(strings.TrimSuffix(cluster.Server, "/"))
if !found {
return "", nil, types.NamespacedName{}, fmt.Errorf("not connected to a control plane or Space")
}
return name, &p, ctp, nil
}
}

return "", nil, types.NamespacedName{}, nil
}

func getIngressHost(ctx context.Context, cfg *rest.Config) (string, error) {
cl, err := client.New(cfg, client.Options{})
if err != nil {
return "", err
}
mxpConfig := &corev1.ConfigMap{}
if err := cl.Get(ctx, types.NamespacedName{Name: "mxp-config", Namespace: "upbound-system"}, mxpConfig); err != nil {
return "", err
}
ingressHost := mxpConfig.Data["ingress.host"]
return ingressHost, nil
}
135 changes: 135 additions & 0 deletions internal/profile/kubeconfig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// Copyright 2024 Upbound Inc
//
// 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 profile

import (
"context"
"fmt"
"net/url"
"strings"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"sigs.k8s.io/controller-runtime/pkg/client"
)

// FromKubeconfig finds the profile by a given user kubeconfig. It returns
// a related profile, the current group/namespace, and the controlplane if the
// kubeconfig points to a controlplane through mxe-router.
func FromKubeconfig(ctx context.Context, profiles map[string]Profile, conf *clientcmdapi.Config) (string, *Profile, types.NamespacedName, error) {
return findProfileByKubeconfig(ctx, profiles, conf, getIngressHost)
}

func findProfileByKubeconfig(ctx context.Context, profiles map[string]Profile, conf *clientcmdapi.Config, getIngressHost func(ctx context.Context, cfg *rest.Config) (string, error)) (string, *Profile, types.NamespacedName, error) { // nolint:gocyclo // TODO: shorten
// get base URL of Upbound profiles
profileURLs := make(map[string]string)
for name, p := range profiles {
if !p.IsSpace() {
continue
}
confCtx, ok := conf.Contexts[p.KubeContext]
if !ok {
continue
}
cluster, ok := conf.Clusters[confCtx.Cluster]
if !ok {
continue
}
profileURLs[name] = cluster.Server
}

// get user cluster URL
confCtx, ok := conf.Contexts[conf.CurrentContext]
if !ok {
return "", nil, types.NamespacedName{}, fmt.Errorf("current context %q not found in kubeconfig", conf.CurrentContext)
}
cluster, ok := conf.Clusters[confCtx.Cluster]
if !ok {
return "", nil, types.NamespacedName{}, fmt.Errorf("cluster %q not found in kubeconfig", confCtx.Cluster)
}

// find profile by URL
for name, profileURL := range profileURLs {
if strings.TrimSuffix(profileURL, "/") == strings.TrimSuffix(cluster.Server, "/") {
p := profiles[name]
ns := confCtx.Namespace
if ns == "" {
ns = corev1.NamespaceDefault
}
return name, &p, types.NamespacedName{Namespace: ns}, nil
}
url, err := url.Parse(profileURL)
if err != nil {
continue
}

// profile points to Spaces API?
if strings.HasPrefix(cluster.Server, strings.TrimSuffix(url.String(), "/")+"/") {
p := profiles[name]

ctp, found := ParseSpacesK8sURL(strings.TrimSuffix(cluster.Server, "/"))
if !found {
return "", nil, types.NamespacedName{}, fmt.Errorf("not connected to a control plane or Space")
}

return name, &p, ctp, nil
}
}

// still not found. Try ingresses.
for name, p := range profiles {
if !p.IsSpace() {
continue
}
pconf := conf.DeepCopy()
pconf.CurrentContext = p.KubeContext
cfg, err := clientcmd.NewDefaultClientConfig(*pconf, &clientcmd.ConfigOverrides{}).ClientConfig()
if err != nil {
continue
}
ingressHost, err := getIngressHost(ctx, cfg)
if err != nil {
continue
}
if !strings.HasPrefix(ingressHost, "https://") {
ingressHost = "https://" + ingressHost
}
if strings.HasPrefix(cluster.Server, strings.TrimSuffix(ingressHost, "/")+"/") {
ctp, found := ParseSpacesK8sURL(strings.TrimSuffix(cluster.Server, "/"))
if !found {
return "", nil, types.NamespacedName{}, fmt.Errorf("not connected to a control plane or Space")
}
return name, &p, ctp, nil
}
}

return "", nil, types.NamespacedName{}, nil
}

func getIngressHost(ctx context.Context, cfg *rest.Config) (string, error) {
cl, err := client.New(cfg, client.Options{})
if err != nil {
return "", err
}
mxpConfig := &corev1.ConfigMap{}
if err := cl.Get(ctx, types.NamespacedName{Name: "mxp-config", Namespace: "upbound-system"}, mxpConfig); err != nil {
return "", err
}
ingressHost := mxpConfig.Data["ingress.host"]
return ingressHost, nil
}
Loading

0 comments on commit da4642b

Please sign in to comment.