Skip to content

Commit

Permalink
feat(fqdn): support using short name access service
Browse files Browse the repository at this point in the history
  • Loading branch information
whalecold committed Dec 28, 2023
1 parent 77bfb1e commit 165ef19
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 11 deletions.
46 changes: 39 additions & 7 deletions core/manager/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package manager
import (
"fmt"
"os"
"strings"
"time"

"github.com/cloudwego/kitex/pkg/klog"
Expand All @@ -30,6 +31,7 @@ import (
const (
PodNamespace = "POD_NAMESPACE"
PodName = "POD_NAME"
MetaNamespace = "NAMESPACE"
InstanceIP = "INSTANCE_IP"
IstiodAddr = "istiod.istio-system.svc:15010"
KitexXdsDomain = "KITEX_XDS_DOMAIN"
Expand All @@ -40,8 +42,12 @@ const (
)

type BootstrapConfig struct {
node *v3core.Node
xdsSvrCfg *XDSServerConfig
// The namespace to make up fqdn.
// Use POD_NAMESPACE default, the meta namespace will override that if set.
configNamespace string
nodeDomain string
node *v3core.Node
xdsSvrCfg *XDSServerConfig
}

type XDSServerConfig struct {
Expand Down Expand Up @@ -88,6 +94,23 @@ func nodeId(podIP, podName, namespace, nodeDomain string) string {
return fmt.Sprintf("sidecar~%s~%s.%s~%s.svc.%s", podIP, podName, namespace, namespace, nodeDomain)
}

// tryExpandFQDN try expand fully qualified domain name.
func (bc *BootstrapConfig) tryExpandFQDN(host string) string {
// The kubernetes services following the <serviceName>.<ns>.svc.<suffix> naming convention
// and that share a suffix with the domain. If it already been expanded, ignore it.
if strings.Contains(host, ".svc.") {
return host
}
var b strings.Builder
b.Grow(len(host) + len(bc.configNamespace) + len(bc.nodeDomain) + 10)
b.WriteString(host)
b.WriteString(".")
b.WriteString(bc.configNamespace)
b.WriteString(".svc.")
b.WriteString(bc.nodeDomain)
return b.String()
}

// newBootstrapConfig constructs the bootstrapConfig
func newBootstrapConfig(config *XDSServerConfig) (*BootstrapConfig, error) {
// Get info from env
Expand All @@ -114,13 +137,22 @@ func newBootstrapConfig(config *XDSServerConfig) (*BootstrapConfig, error) {
nodeDomain = "cluster.local"
}

metaEnvs := os.Getenv(KitexXdsMetas)

return &BootstrapConfig{
bsConfig := &BootstrapConfig{
nodeDomain: nodeDomain,
configNamespace: namespace,
node: &v3core.Node{
Id: nodeId(podIP, podName, namespace, nodeDomain),
Metadata: parseMetaEnvs(metaEnvs, istioVersion),
Metadata: parseMetaEnvs(os.Getenv(KitexXdsMetas), istioVersion),
},
xdsSvrCfg: config,
}, nil
}

// the priority of NAMESPACE in metadata is higher than POD_NAMESPACE.
// ref: https://github.com/istio/istio/blob/30446a7b88aba4a0fcd5f71bae8d397a874e846f/pilot/pkg/model/context.go#L1024
if field, ok := bsConfig.node.Metadata.Fields[MetaNamespace]; ok {
if val := field.GetStringValue(); val != "" {
bsConfig.configNamespace = val
}
}
return bsConfig, nil
}
28 changes: 24 additions & 4 deletions core/manager/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,27 @@ func (c *xdsClient) sendRequest(req *discoveryv3.DiscoveryRequest) {
c.reqCh <- req
}

func (c *xdsClient) resolveAddr(host string) string {
// In the worst case, lookupHost is called twice, try to reduce it.
// May exists three kind host:
// 1. fqdn host in Kubernetes, such as example.default.svc.cluster.local, invoke once always.
// 2. short name in Kubernetes, such as example, invoke once when the host exists in cipResolver, and twice when the host does not exist in cipResolver.
// 3. service outside Kubernetes, such as www.example.com, invoke twice always.
// FIXME: format as <serviceName>.<namespace> is not supported.
fqdn := c.config.tryExpandFQDN(host)
cip, ok := c.cipResolver.lookupHost(fqdn)
if ok && len(cip) > 0 {
return cip[0]
}
if fqdn != host {
cip, ok := c.cipResolver.lookupHost(host)
if ok && len(cip) > 0 {
return cip[0]
}
}
return ""
}

// getListenerName returns the listener name in this format: ${clusterIP}_${port}
// lookup the clusterIP using the cipResolver and return the listenerName
func (c *xdsClient) getListenerName(rName string) (string, error) {
Expand All @@ -393,10 +414,9 @@ func (c *xdsClient) getListenerName(rName string) (string, error) {
return "", fmt.Errorf("invalid listener name: %s", rName)
}
addr, port := tmp[0], tmp[1]
cip, ok := c.cipResolver.lookupHost(addr)
if ok && len(cip) > 0 {
clusterIPPort := cip[0] + "_" + port
return clusterIPPort, nil
cip := c.resolveAddr(addr)
if len(cip) > 0 {
return cip + "_" + port, nil
}
return "", fmt.Errorf("failed to convert listener name for %s", rName)
}
Expand Down
42 changes: 42 additions & 0 deletions core/manager/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,3 +218,45 @@ func TestClearCh(t *testing.T) {
clearRequestCh(ch, 10)
assert.Equal(t, 0, len(ch))
}

func TestResolveAddr(t *testing.T) {
resolver := newNdsResolver()
resolver.updateLookupTable(map[string][]string{
"echoa.default.svc.cluster.local": {"1.1.1.1"},
"echob.default.svc.cluster.local": {"1.1.1.1"},
})
cli := &xdsClient{
cipResolver: resolver,
config: &BootstrapConfig{
configNamespace: "default",
nodeDomain: "cluster.local",
},
}
testCases := []struct {
desc string
host string
want string
}{
{
desc: "fqdn",
host: "echoa.default.svc.cluster.local",
want: "1.1.1.1",
},
{
desc: "short name",
host: "echoa",
want: "1.1.1.1",
},
{
desc: "not found",
host: "echoa.default",
want: "",
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
got := cli.resolveAddr(tc.host)
assert.Equal(t, tc.want, got)
})
}
}

0 comments on commit 165ef19

Please sign in to comment.