Skip to content

Commit 4010419

Browse files
authored
Merge pull request #57 from yue9944882/bugfix/support-absent-port-addr
Bugfix: Support absent port in the address
2 parents f0d658b + f418c2c commit 4010419

File tree

2 files changed

+229
-16
lines changed

2 files changed

+229
-16
lines changed

pkg/apis/cluster/v1alpha1/transport.go

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,33 @@ import (
1010
"github.com/pkg/errors"
1111
"google.golang.org/grpc"
1212
grpccredentials "google.golang.org/grpc/credentials"
13+
k8snet "k8s.io/apimachinery/pkg/util/net"
1314
restclient "k8s.io/client-go/rest"
1415
konnectivity "sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/client"
1516
"sigs.k8s.io/apiserver-network-proxy/pkg/util"
1617
)
1718

19+
var DialerGetter = func() (k8snet.DialFunc, error) {
20+
tlsCfg, err := util.GetClientTLSConfig(
21+
config.ClusterProxyCAFile,
22+
config.ClusterProxyCertFile,
23+
config.ClusterProxyKeyFile,
24+
config.ClusterProxyHost,
25+
nil)
26+
if err != nil {
27+
return nil, err
28+
}
29+
tunnel, err := konnectivity.CreateSingleUseGrpcTunnel(
30+
context.TODO(),
31+
net.JoinHostPort(config.ClusterProxyHost, strconv.Itoa(config.ClusterProxyPort)),
32+
grpc.WithTransportCredentials(grpccredentials.NewTLS(tlsCfg)),
33+
)
34+
if err != nil {
35+
return nil, err
36+
}
37+
return tunnel.DialContext, nil
38+
}
39+
1840
func NewConfigFromCluster(c *ClusterGateway) (*restclient.Config, error) {
1941
cfg := &restclient.Config{}
2042
// setting up endpoint
@@ -29,33 +51,29 @@ func NewConfigFromCluster(c *ClusterGateway) (*restclient.Config, error) {
2951
if err != nil {
3052
return nil, err
3153
}
54+
55+
const missingPort = "missing port in address"
3256
host, _, err := net.SplitHostPort(u.Host)
3357
if err != nil {
34-
return nil, err
58+
addrErr, ok := err.(*net.AddrError)
59+
if !ok {
60+
return nil, err
61+
}
62+
if addrErr.Err != missingPort {
63+
return nil, err
64+
}
65+
host = u.Host
3566
}
3667
cfg.ServerName = host // apiserver may listen on SNI cert
3768
case ClusterEndpointTypeClusterProxy:
3869
cfg.Host = c.Name // the same as the cluster name
3970
cfg.Insecure = true
4071
cfg.CAData = nil
41-
tlsCfg, err := util.GetClientTLSConfig(
42-
config.ClusterProxyCAFile,
43-
config.ClusterProxyCertFile,
44-
config.ClusterProxyKeyFile,
45-
config.ClusterProxyHost,
46-
nil)
47-
if err != nil {
48-
return nil, err
49-
}
50-
tunnel, err := konnectivity.CreateSingleUseGrpcTunnel(
51-
context.TODO(),
52-
net.JoinHostPort(config.ClusterProxyHost, strconv.Itoa(config.ClusterProxyPort)),
53-
grpc.WithTransportCredentials(grpccredentials.NewTLS(tlsCfg)),
54-
)
72+
dail, err := DialerGetter()
5573
if err != nil {
5674
return nil, err
5775
}
58-
cfg.Dial = tunnel.DialContext
76+
cfg.Dial = dail
5977
}
6078
// setting up credentials
6179
switch c.Spec.Access.Credential.Type {
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
package v1alpha1
2+
3+
import (
4+
"context"
5+
"net"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11+
k8snet "k8s.io/apimachinery/pkg/util/net"
12+
"k8s.io/client-go/rest"
13+
"k8s.io/utils/pointer"
14+
)
15+
16+
func TestClusterRestConfigConversion(t *testing.T) {
17+
testToken := "test-token"
18+
testCAData := []byte(`test-ca`)
19+
testCertData := []byte(`test-cert`)
20+
testKeyData := []byte(`test-key`)
21+
testDialFunc := func(ctx context.Context, net, addr string) (net.Conn, error) {
22+
return nil, nil
23+
}
24+
DialerGetter = func() (k8snet.DialFunc, error) {
25+
return testDialFunc, nil
26+
}
27+
cases := []struct {
28+
name string
29+
clusterGateway *ClusterGateway
30+
expectedCfg *rest.Config
31+
expectFailure bool
32+
}{
33+
{
34+
name: "normal cluster-gateway with SA token + host-port should work",
35+
clusterGateway: &ClusterGateway{
36+
Spec: ClusterGatewaySpec{
37+
Access: ClusterAccess{
38+
Endpoint: &ClusterEndpoint{
39+
Type: ClusterEndpointTypeConst,
40+
Const: &ClusterEndpointConst{
41+
Address: "https://foo.bar:33",
42+
CABundle: testCAData,
43+
},
44+
},
45+
Credential: &ClusterAccessCredential{
46+
Type: CredentialTypeServiceAccountToken,
47+
ServiceAccountToken: testToken,
48+
},
49+
},
50+
},
51+
},
52+
expectedCfg: &rest.Config{
53+
Host: "https://foo.bar:33",
54+
BearerToken: testToken,
55+
TLSClientConfig: rest.TLSClientConfig{
56+
ServerName: "foo.bar",
57+
CAData: testCAData,
58+
},
59+
},
60+
},
61+
{
62+
name: "normal cluster-gateway with X509 + host-port should work",
63+
clusterGateway: &ClusterGateway{
64+
Spec: ClusterGatewaySpec{
65+
Access: ClusterAccess{
66+
Endpoint: &ClusterEndpoint{
67+
Type: ClusterEndpointTypeConst,
68+
Const: &ClusterEndpointConst{
69+
Address: "https://foo.bar:33",
70+
CABundle: testCAData,
71+
},
72+
},
73+
Credential: &ClusterAccessCredential{
74+
Type: CredentialTypeX509Certificate,
75+
X509: &X509{
76+
Certificate: testCertData,
77+
PrivateKey: testKeyData,
78+
},
79+
},
80+
},
81+
},
82+
},
83+
expectedCfg: &rest.Config{
84+
Host: "https://foo.bar:33",
85+
TLSClientConfig: rest.TLSClientConfig{
86+
ServerName: "foo.bar",
87+
CAData: testCAData,
88+
CertData: testCertData,
89+
KeyData: testKeyData,
90+
},
91+
},
92+
},
93+
{
94+
name: "https port defaulting should work",
95+
clusterGateway: &ClusterGateway{
96+
Spec: ClusterGatewaySpec{
97+
Access: ClusterAccess{
98+
Endpoint: &ClusterEndpoint{
99+
Type: ClusterEndpointTypeConst,
100+
Const: &ClusterEndpointConst{
101+
Address: "https://foo.bar",
102+
CABundle: testCAData,
103+
},
104+
},
105+
Credential: &ClusterAccessCredential{
106+
Type: CredentialTypeServiceAccountToken,
107+
ServiceAccountToken: testToken,
108+
},
109+
},
110+
},
111+
},
112+
expectedCfg: &rest.Config{
113+
Host: "https://foo.bar",
114+
BearerToken: testToken,
115+
TLSClientConfig: rest.TLSClientConfig{
116+
ServerName: "foo.bar",
117+
CAData: testCAData,
118+
},
119+
},
120+
},
121+
{
122+
name: "insecure (no CA bundle) should work",
123+
clusterGateway: &ClusterGateway{
124+
Spec: ClusterGatewaySpec{
125+
Access: ClusterAccess{
126+
Endpoint: &ClusterEndpoint{
127+
Type: ClusterEndpointTypeConst,
128+
Const: &ClusterEndpointConst{
129+
Address: "https://foo.bar:33",
130+
Insecure: pointer.Bool(true),
131+
},
132+
},
133+
Credential: &ClusterAccessCredential{
134+
Type: CredentialTypeServiceAccountToken,
135+
ServiceAccountToken: testToken,
136+
},
137+
},
138+
},
139+
},
140+
expectedCfg: &rest.Config{
141+
Host: "https://foo.bar:33",
142+
BearerToken: testToken,
143+
TLSClientConfig: rest.TLSClientConfig{
144+
ServerName: "foo.bar",
145+
Insecure: true,
146+
},
147+
},
148+
},
149+
{
150+
name: "cluster-proxy egress should work",
151+
clusterGateway: &ClusterGateway{
152+
ObjectMeta: metav1.ObjectMeta{
153+
Name: "my-cluster",
154+
},
155+
Spec: ClusterGatewaySpec{
156+
Access: ClusterAccess{
157+
Endpoint: &ClusterEndpoint{
158+
Type: ClusterEndpointTypeClusterProxy,
159+
},
160+
Credential: &ClusterAccessCredential{
161+
Type: CredentialTypeServiceAccountToken,
162+
ServiceAccountToken: testToken,
163+
},
164+
},
165+
},
166+
},
167+
expectedCfg: &rest.Config{
168+
Host: "my-cluster",
169+
BearerToken: testToken,
170+
Dial: testDialFunc,
171+
TLSClientConfig: rest.TLSClientConfig{
172+
Insecure: true,
173+
},
174+
},
175+
},
176+
}
177+
for _, c := range cases {
178+
t.Run(c.name, func(t *testing.T) {
179+
cfg, err := NewConfigFromCluster(c.clusterGateway)
180+
if err != nil {
181+
if c.expectFailure {
182+
return
183+
}
184+
require.NoError(t, err)
185+
}
186+
if cfg.Dial != nil {
187+
assert.ObjectsAreEqual(c.expectedCfg.Dial, cfg.Dial)
188+
c.expectedCfg.Dial = nil
189+
cfg.Dial = nil
190+
}
191+
assert.Equal(t, c.expectedCfg, cfg)
192+
})
193+
}
194+
195+
}

0 commit comments

Comments
 (0)