-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
396 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
/* | ||
Copyright © 2024 Red Hat | ||
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 zuul | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"os" | ||
"strings" | ||
|
||
cliutils "github.com/softwarefactory-project/sf-operator/cli/cmd/utils" | ||
"github.com/spf13/cobra" | ||
ctrl "sigs.k8s.io/controller-runtime" | ||
) | ||
|
||
var ( | ||
zuulGetAllowedArgs = []string{"client-config", "auth-token"} | ||
) | ||
|
||
func zuulCreate(kmd *cobra.Command, args []string) { | ||
cliCtx := cliutils.GetCLIctxOrDie(kmd, args, zuulGetAllowedArgs) | ||
target := args[0] | ||
ns := cliCtx.Namespace | ||
kubeContext := cliCtx.KubeContext | ||
authConfig, _ := kmd.Flags().GetString("auth-config") | ||
tenant, _ := kmd.Flags().GetString("tenant") | ||
user, _ := kmd.Flags().GetString("user") | ||
expiry, _ := kmd.Flags().GetInt("expires-in") | ||
if target == "auth-token" { | ||
if tenant == "" { | ||
ctrl.Log.Error(errors.New("missing argument"), "A tenant is required") | ||
os.Exit(1) | ||
} | ||
token := CreateAuthToken(kubeContext, ns, authConfig, tenant, user, expiry) | ||
fmt.Println(token) | ||
} | ||
if target == "client-config" { | ||
insecure, _ := kmd.Flags().GetBool("insecure") | ||
fqdn := cliCtx.FQDN | ||
config := CreateClientConfig(kubeContext, ns, fqdn, authConfig, tenant, user, expiry, !insecure) | ||
fmt.Println(config) | ||
} | ||
} | ||
|
||
func MkZuulCmd() *cobra.Command { | ||
var ( | ||
authConfig string | ||
tenant string | ||
user string | ||
expiry int | ||
insecure bool | ||
zuulCmd = &cobra.Command{ | ||
Use: "zuul", | ||
Short: "Zuul subcommands", | ||
Long: "These subcommands can be used to interact with the Zuul component of a Software Factory deployment", | ||
} | ||
createCmd, _, _ = cliutils.GetCRUDSubcommands() | ||
) | ||
|
||
createCmd.Run = zuulCreate | ||
createCmd.Use = "create {" + strings.Join(zuulGetAllowedArgs, ", ") + "}" | ||
createCmd.Long = "Create a Zuul resource: an authentication token or a CLI configuration file" | ||
createCmd.ValidArgs = zuulGetAllowedArgs | ||
createCmd.Flags().StringVar(&authConfig, "auth-config", "zuul_client", "the local authentication config to use to generate a token") | ||
createCmd.Flags().StringVar(&tenant, "tenant", "", "the tenant on which the token should grant admin access") | ||
createCmd.Flags().StringVar(&user, "user", "John Doe", "a username, only used for audit purposes in Zuul's access logs") | ||
createCmd.Flags().IntVar(&expiry, "expires-in", 3600, "how long the authentication token should be valid for") | ||
createCmd.Flags().BoolVar(&insecure, "insecure", false, "do not verify SSL certificates when connection to Zuul") | ||
|
||
zuulCmd.AddCommand(createCmd) | ||
return zuulCmd | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
/* | ||
Copyright © 2024 Red Hat | ||
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 zuul deals with zuul-related subcommands. | ||
package zuul | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"crypto/tls" | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"io" | ||
"net/http" | ||
"os" | ||
"strings" | ||
|
||
"github.com/softwarefactory-project/sf-operator/controllers" | ||
v1 "k8s.io/api/core/v1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/client-go/kubernetes" | ||
"k8s.io/client-go/rest" | ||
"k8s.io/client-go/tools/remotecommand" | ||
"k8s.io/kubectl/pkg/scheme" | ||
|
||
ctrl "sigs.k8s.io/controller-runtime" | ||
) | ||
|
||
type ZuulAPITenant struct { | ||
Name string `json:"name"` | ||
Projects int `json:"projects"` | ||
Queue int `json:"queue"` | ||
} | ||
|
||
func GetTenants(fqdn string, verify bool) []string { | ||
tr := &http.Transport{ | ||
TLSClientConfig: &tls.Config{ | ||
InsecureSkipVerify: !verify, | ||
}, | ||
} | ||
client := &http.Client{Transport: tr} | ||
tenantsURL := "https://" + fqdn + "/zuul/api/tenants" | ||
resp, err := client.Get(tenantsURL) | ||
if err != nil { | ||
ctrl.Log.Error(err, "HTTP protocol error") | ||
os.Exit(1) | ||
} | ||
if resp.StatusCode >= 400 { | ||
ctrl.Log.Error(errors.New("bad status"), "API returned status "+resp.Status) | ||
os.Exit(1) | ||
} | ||
defer resp.Body.Close() | ||
body, err := io.ReadAll(resp.Body) | ||
if err != nil { | ||
ctrl.Log.Error(err, "Error reading API response") | ||
os.Exit(1) | ||
} | ||
_tenants := []ZuulAPITenant{} | ||
err = json.Unmarshal(body, &_tenants) | ||
if err != nil { | ||
ctrl.Log.Error(err, "Error marshalling JSON response") | ||
os.Exit(1) | ||
} | ||
tenants := []string{} | ||
for _, tenant := range _tenants { | ||
tenants = append(tenants, tenant.Name) | ||
} | ||
return tenants | ||
} | ||
|
||
func getClientset(kubeContext string) (*rest.Config, *kubernetes.Clientset) { | ||
restConfig := controllers.GetConfigContextOrDie(kubeContext) | ||
kubeClientset, err := kubernetes.NewForConfig(restConfig) | ||
if err != nil { | ||
ctrl.Log.Error(err, "Could not instantiate Clientset") | ||
os.Exit(1) | ||
} | ||
return restConfig, kubeClientset | ||
} | ||
|
||
func getFirstPod(prefix string, namespace string, kubeContext string) *v1.Pod { | ||
var ctr *v1.Pod = nil | ||
|
||
_, kubeClientset := getClientset(kubeContext) | ||
|
||
podslist, _ := kubeClientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{}) | ||
for _, container := range podslist.Items { | ||
if strings.HasPrefix(container.Name, prefix) { | ||
ctr = &container | ||
break | ||
} | ||
} | ||
return ctr | ||
} | ||
|
||
func runRemoteCmd(kubeContext string, namespace string, podName string, containerName string, cmdArgs []string) *bytes.Buffer { | ||
restConfig, kubeClientset := getClientset(kubeContext) | ||
buffer := &bytes.Buffer{} | ||
errorBuffer := &bytes.Buffer{} | ||
request := kubeClientset.CoreV1().RESTClient().Post().Resource("Pods").Namespace(namespace).Name(podName).SubResource("exec").VersionedParams( | ||
&v1.PodExecOptions{ | ||
Container: containerName, | ||
Command: cmdArgs, | ||
Stdin: false, | ||
Stdout: true, | ||
Stderr: true, | ||
}, | ||
scheme.ParameterCodec, | ||
) | ||
exec, _ := remotecommand.NewSPDYExecutor(restConfig, "POST", request.URL()) | ||
err := exec.StreamWithContext(context.Background(), remotecommand.StreamOptions{ | ||
Stdout: buffer, | ||
Stderr: errorBuffer, | ||
}) | ||
if err != nil { | ||
errMsg := fmt.Sprintf("Command \"%s\" [Pod: %s - Container: %s] failed with the following stderr: %s", | ||
strings.Join(cmdArgs, " "), podName, containerName, errorBuffer.String()) | ||
ctrl.Log.Error(err, errMsg) | ||
os.Exit(1) | ||
} | ||
return buffer | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
/* | ||
Copyright © 2024 Red Hat | ||
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 zuul | ||
|
||
import "strconv" | ||
|
||
// zuul-admin proxy commands. | ||
|
||
func CreateAuthToken(kubeContext string, namespace string, authConfig string, tenant string, user string, expiry int) string { | ||
_authConfig := authConfig | ||
if _authConfig == "" { | ||
_authConfig = "zuul_client" | ||
} | ||
scheduler := getFirstPod("zuul-scheduler", namespace, kubeContext) | ||
createAuthTokenCmd := []string{ | ||
"zuul-admin", | ||
"create-auth-token", | ||
"--auth-config", _authConfig, | ||
"--tenant", tenant, | ||
"--user", user, | ||
"--expires-in", strconv.Itoa(expiry), | ||
} | ||
token := runRemoteCmd(kubeContext, namespace, scheduler.Name, "zuul-scheduler", createAuthTokenCmd) | ||
return token.String() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
/* | ||
Copyright © 2024 Red Hat | ||
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 zuul | ||
|
||
import ( | ||
"bytes" | ||
"os" | ||
"strings" | ||
"text/template" | ||
|
||
ctrl "sigs.k8s.io/controller-runtime" | ||
) | ||
|
||
type ZuulClientConfigSection struct { | ||
Name string | ||
URL string | ||
Tenant string | ||
VerifySSL bool | ||
AuthToken string | ||
} | ||
|
||
var configTemplate = ` | ||
[{{ .Name }}] | ||
url={{ .URL }} | ||
{{ if .Tenant }}tenant={{ .Tenant }}{{ else }}{{ end }} | ||
verify_ssl={{ if .VerifySSL }}True{{ else }}False{{ end }} | ||
{{ if .AuthToken }}auth_token={{ .AuthToken }}{{ else }}{{ end }} | ||
` | ||
|
||
func CreateClientConfig(kubeContext string, namespace string, fqdn string, authConfig string, tenant string, user string, expiry int, verify bool) string { | ||
var tenants []string | ||
if tenant != "" { | ||
tenants = []string{tenant} | ||
} else { | ||
tenants = GetTenants(fqdn, verify) | ||
} | ||
var config string | ||
for _, t := range tenants { | ||
token := CreateAuthToken(kubeContext, namespace, authConfig, t, user, expiry) | ||
section := ZuulClientConfigSection{ | ||
Name: t, | ||
URL: "https://" + fqdn + "/zuul", | ||
Tenant: t, | ||
VerifySSL: verify, | ||
AuthToken: strings.TrimPrefix(token, "Bearer "), | ||
} | ||
confTemplate, err := template.New("configSection").Parse(configTemplate) | ||
if err != nil { | ||
ctrl.Log.Error(err, "Error initializing config template") | ||
os.Exit(1) | ||
} | ||
var buf bytes.Buffer | ||
if err := confTemplate.Execute(&buf, section); err != nil { | ||
ctrl.Log.Error(err, "Error applying config template") | ||
os.Exit(1) | ||
} | ||
config += buf.String() | ||
} | ||
return config | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.