Skip to content

Commit a6a8ada

Browse files
committed
Create packages for connecting to a cluster
1 parent 75c15b5 commit a6a8ada

File tree

4 files changed

+263
-4
lines changed

4 files changed

+263
-4
lines changed

cmd/cluster-version-operator/main.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package main
22

33
import (
44
"flag"
5+
"fmt"
56

7+
utilities "github.com/openshift/cluster-version-operator/test/utilities"
68
"github.com/spf13/cobra"
79
"k8s.io/klog/v2"
810
)
@@ -22,8 +24,17 @@ func init() {
2224
}
2325

2426
func main() {
25-
defer klog.Flush()
26-
if err := rootCmd.Execute(); err != nil {
27-
klog.Exitf("Error executing: %v", err)
27+
server, folder, err := utilities.GetConnection("admin")
28+
if err != nil {
29+
fmt.Printf("Error creating request: %v\n", err)
2830
}
31+
transport, err := utilities.GetCert(folder)
32+
if err != nil {
33+
fmt.Printf("Error creating certificate: %v\n", err)
34+
}
35+
// utilities.GetResorce(server, transport, "/apis/config.openshift.io/v1/clusterversions/version")
36+
37+
jsonObject := "{\"spec\":{\"capabilities\":{\"baselineCapabilitySet\":\"None\"}}}"
38+
newResource, _ := utilities.PatchResorce(server, transport, "/apis/config.openshift.io/v1/clusterversions/version", jsonObject)
39+
fmt.Println(newResource)
2940
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ require (
2424
golang.org/x/net v0.38.0
2525
golang.org/x/time v0.9.0
2626
gopkg.in/fsnotify.v1 v1.4.7
27+
gopkg.in/yaml.v2 v2.4.0
2728
k8s.io/api v0.33.2
2829
k8s.io/apiextensions-apiserver v0.32.1
2930
k8s.io/apimachinery v0.33.2
@@ -78,7 +79,6 @@ require (
7879
google.golang.org/protobuf v1.36.5 // indirect
7980
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
8081
gopkg.in/inf.v0 v0.9.1 // indirect
81-
gopkg.in/yaml.v2 v2.4.0 // indirect
8282
gopkg.in/yaml.v3 v3.0.1 // indirect
8383
k8s.io/apiserver v0.32.1 // indirect
8484
k8s.io/component-base v0.32.1 // indirect

test/utilities/api_helper.go

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package utilities
2+
3+
import (
4+
"crypto/tls"
5+
"crypto/x509"
6+
"errors"
7+
"fmt"
8+
"io"
9+
"log"
10+
"net/http"
11+
"net/url"
12+
"os"
13+
"strings"
14+
)
15+
16+
// GetCert
17+
// @Description get certificate for URL request
18+
// @Create jianl Oct 22 2025
19+
// @Param kubeConfigFolder string The certificate folder which contains key.crt, client.crt, ca.ct
20+
// @Return (Instance of http.Transport, error)
21+
func GetCert(kubeConfigFolder string) (*http.Transport, error) {
22+
// 1. Load the client certificate and private key
23+
clientCert, err := tls.LoadX509KeyPair(kubeConfigFolder+"/client.crt", kubeConfigFolder+"/key.crt")
24+
if err != nil {
25+
log.Fatalf("Error loading client certificate and key: %v", err)
26+
return nil, errors.New("Error loading client certificate and key")
27+
}
28+
29+
// 2. Load the CA certificate(s) to trust a custom CA
30+
caCert, err := os.ReadFile(kubeConfigFolder + "/ca.ct")
31+
if err != nil {
32+
log.Fatalf("Error loading CA certificate: %v.", err)
33+
return nil, errors.New("Error loading CA certificate")
34+
}
35+
caCertPool := x509.NewCertPool()
36+
if caCert != nil {
37+
caCertPool.AppendCertsFromPEM(caCert)
38+
}
39+
40+
// 3. Create a tls.Config with the client certificate and trusted CAs
41+
tlsConfig := &tls.Config{
42+
Certificates: []tls.Certificate{clientCert},
43+
RootCAs: caCertPool, // Only needed if you have a custom CA
44+
InsecureSkipVerify: true,
45+
}
46+
47+
transport := &http.Transport{
48+
TLSClientConfig: tlsConfig,
49+
}
50+
return transport, nil
51+
}
52+
53+
// GetResorce
54+
// @Description get resources from an Openshift cluster
55+
// @Create jianl Oct 22 2025
56+
// @Param server string API host
57+
// @Param transport string Instance of http.Transport which used to create http.Client
58+
// @Param api string The api endpoint
59+
// @Return (resource, error)
60+
func GetResorce(server string, transport *http.Transport, api string) (string, error) {
61+
finalUrl, err := url.JoinPath(server, api)
62+
if err != nil {
63+
fmt.Printf("Error failed to concatenate URL for request: %v\n", err)
64+
return "", err
65+
}
66+
67+
// Create a new GET request
68+
client := &http.Client{
69+
Transport: transport,
70+
}
71+
72+
resp, err := client.Get(finalUrl) // Replace with your URL
73+
if err != nil {
74+
fmt.Printf("Error creating request: %v\n", err)
75+
return "", err
76+
}
77+
defer func() {
78+
if err := resp.Body.Close(); err != nil {
79+
log.Printf("Error closing response body: %v", err)
80+
}
81+
}()
82+
83+
// Send the request
84+
body, err := io.ReadAll(resp.Body)
85+
if err != nil {
86+
fmt.Printf("Error reading response body: %v\n", err)
87+
return "", err
88+
}
89+
return string(body), nil
90+
}
91+
92+
// PatchResorce
93+
// @Description Patch update resource
94+
// @Create jianl Oct 22 2025
95+
// @Param server string API host
96+
// @Param transport string Instance of http.Transport which used to create http.Client
97+
// @Param api string The api endpoint
98+
// @Param jsonObject string The part of resources which will be updated
99+
//
100+
// (Now only support json format string, for example: "{\"spec\":{\"capabilities\":{\"baselineCapabilitySet\":\"None\"}}}")
101+
//
102+
// @Return (resource, error)
103+
func PatchResorce(server string, transport *http.Transport, api string, jsonObject string) (string, error) {
104+
finalUrl, err := url.JoinPath(server, api)
105+
if err != nil {
106+
fmt.Printf("Error failed to concatenate URL for request: %v\n", err)
107+
return "", err
108+
}
109+
// Create a new GET request
110+
client := &http.Client{
111+
Transport: transport,
112+
}
113+
114+
req, err := http.NewRequest(http.MethodPatch, finalUrl, strings.NewReader(jsonObject))
115+
if err != nil {
116+
log.Fatal(err)
117+
}
118+
req.Header.Set("Content-Type", "application/merge-patch+json")
119+
resp, err := client.Do(req) // Replace with your URL
120+
if err != nil {
121+
fmt.Printf("Error creating request: %v\n", err)
122+
return "", err
123+
}
124+
defer func() {
125+
if err := resp.Body.Close(); err != nil {
126+
log.Printf("Error closing response body: %v", err)
127+
}
128+
}()
129+
130+
// Send the request
131+
body, err := io.ReadAll(resp.Body)
132+
if err != nil {
133+
fmt.Printf("Error reading response body: %v\n", err)
134+
return "", err
135+
}
136+
return string(body), nil
137+
}

test/utilities/connection.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package utilities
2+
3+
import (
4+
"encoding/base64"
5+
"errors"
6+
"fmt"
7+
"os"
8+
"path/filepath"
9+
10+
"gopkg.in/yaml.v2"
11+
)
12+
13+
type Cluster struct {
14+
Name string `yaml:"name"`
15+
Cert ClusterCert `yaml:"cluster"`
16+
}
17+
18+
type ClusterCert struct {
19+
CertificateAuthorityData string `yaml:"certificate-authority-data"`
20+
Server string `yaml:"server"`
21+
}
22+
23+
type User struct {
24+
Name string `yaml:"name"`
25+
Cert UserCert `yaml:"user"`
26+
}
27+
28+
type UserCert struct {
29+
ClientCertificateData string `yaml:"client-certificate-data"`
30+
ClientKeyData string `yaml:"client-key-data"`
31+
}
32+
33+
type KubeConfig struct {
34+
Clusters []Cluster `yaml:"clusters"`
35+
Users []User `yaml:"users"`
36+
}
37+
38+
// getConnection
39+
// @Description get connection object which can help us connect to a Openshift cluster
40+
// @Create jianl Oct 22 2025
41+
// @Param user string the cluster's user name which can be found in Kubeconfig file, like admin
42+
// @Return (API host, kube config folder (In which there are three files: key.crt, client.crt, ca.ct), error)
43+
func GetConnection(user string) (server string, KubeConfigFolder string, err error) {
44+
if user == "" {
45+
user = "admin"
46+
}
47+
48+
configPath, present := os.LookupEnv("KUBECONFIG")
49+
if present {
50+
tmpDir, err := os.MkdirTemp("", "kubeconfig")
51+
if err != nil {
52+
fmt.Println("Error could not create certficates folder:", err)
53+
return "", "", err
54+
}
55+
56+
data, err := os.ReadFile(configPath)
57+
if err != nil {
58+
fmt.Println("Error reading file:", err)
59+
return "", "", err
60+
}
61+
var config KubeConfig
62+
err = yaml.Unmarshal(data, &config)
63+
if err != nil {
64+
fmt.Println("Error unmarshaling JSON:", err)
65+
return "", "", err
66+
}
67+
server := config.Clusters[0].Cert.Server
68+
ca, err := base64.StdEncoding.DecodeString(config.Clusters[0].Cert.CertificateAuthorityData)
69+
if err != nil {
70+
fmt.Println("Error decoding certificate-authority-data:", err)
71+
return "", "", err
72+
}
73+
err = os.WriteFile(filepath.Join(tmpDir, "ca.ct"), ca, 0644)
74+
if err != nil {
75+
return "", "", err
76+
}
77+
78+
for _, v := range config.Users {
79+
if v.Name == user {
80+
cert, err := base64.StdEncoding.DecodeString(v.Cert.ClientCertificateData)
81+
if err != nil {
82+
fmt.Println("Error decoding client-certificate-data:", err)
83+
return "", "", err
84+
}
85+
err = os.WriteFile(filepath.Join(tmpDir, "client.crt"), cert, 0644)
86+
if err != nil {
87+
return "", "", err
88+
}
89+
key, err := base64.StdEncoding.DecodeString(v.Cert.ClientKeyData)
90+
if err != nil {
91+
fmt.Println("Error decoding client-key-data:", err)
92+
return "", "", err
93+
}
94+
err = os.WriteFile(filepath.Join(tmpDir, "key.crt"), key, 0644)
95+
if err != nil {
96+
return "", "", err
97+
}
98+
}
99+
}
100+
101+
return server, tmpDir, err
102+
103+
} else {
104+
return "", "", errors.New("KUBECONFIG not set")
105+
}
106+
}
107+
108+
// func main() {
109+
// conn, _ := getConnection("admin")
110+
// fmt.Println(conn)
111+
// }

0 commit comments

Comments
 (0)