Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 42 additions & 18 deletions pkg/cli/create/routeedge.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,25 @@ var (
# Create an edge route that exposes the frontend service and specify a path
# If the route name is omitted, the service name will be used
oc create route edge --service=frontend --path /assets

# Create an edge route that uses an external certificate from a secret
oc create route edge --service=frontend --external-certificate=my-cert-secret
`)
)

type CreateEdgeRouteOptions struct {
CreateRouteSubcommandOptions *CreateRouteSubcommandOptions

Hostname string
Port string
InsecurePolicy string
Service string
Path string
Cert string
Key string
CACert string
WildcardPolicy string
Hostname string
Port string
InsecurePolicy string
Service string
Path string
Cert string
Key string
CACert string
ExternalCertificate string
WildcardPolicy string
}

// NewCmdCreateEdgeRoute is a macro command to create an edge route.
Expand All @@ -63,6 +67,7 @@ func NewCmdCreateEdgeRoute(f kcmdutil.Factory, streams genericiooptions.IOStream
Example: edgeRouteExample,
Run: func(cmd *cobra.Command, args []string) {
kcmdutil.CheckErr(o.Complete(f, cmd, args))
kcmdutil.CheckErr(o.Validate())
kcmdutil.CheckErr(o.Run())
},
}
Expand All @@ -79,6 +84,7 @@ func NewCmdCreateEdgeRoute(f kcmdutil.Factory, streams genericiooptions.IOStream
cmd.MarkFlagFilename("key")
cmd.Flags().StringVar(&o.CACert, "ca-cert", o.CACert, "Path to a CA certificate file.")
cmd.MarkFlagFilename("ca-cert")
cmd.Flags().StringVar(&o.ExternalCertificate, "external-certificate", o.ExternalCertificate, "Name of a secret that contains the TLS certificate and key. The secret must contain keys named tls.crt and tls.key. Mutually exclusive with --cert and --key.")
cmd.Flags().StringVar(&o.WildcardPolicy, "wildcard-policy", o.WildcardPolicy, "Sets the WilcardPolicy for the hostname, the default is \"None\". valid values are \"None\" and \"Subdomain\"")

kcmdutil.AddValidateFlags(cmd)
Expand All @@ -92,6 +98,16 @@ func (o *CreateEdgeRouteOptions) Complete(f kcmdutil.Factory, cmd *cobra.Command
return o.CreateRouteSubcommandOptions.Complete(f, cmd, args)
}

func (o *CreateEdgeRouteOptions) Validate() error {
if len(o.Cert) > 0 && len(o.ExternalCertificate) > 0 {
return fmt.Errorf("--cert and --external-certificate are mutually exclusive")
}
if len(o.Key) > 0 && len(o.ExternalCertificate) > 0 {
return fmt.Errorf("--key and --external-certificate are mutually exclusive")
}
return nil
}

func (o *CreateEdgeRouteOptions) Run() error {
serviceName, err := resolveServiceName(o.CreateRouteSubcommandOptions.Mapper, o.Service)
if err != nil {
Expand All @@ -111,16 +127,24 @@ func (o *CreateEdgeRouteOptions) Run() error {

route.Spec.TLS = new(routev1.TLSConfig)
route.Spec.TLS.Termination = routev1.TLSTerminationEdge
cert, err := fileutil.LoadData(o.Cert)
if err != nil {
return err
}
route.Spec.TLS.Certificate = string(cert)
key, err := fileutil.LoadData(o.Key)
if err != nil {
return err

if len(o.ExternalCertificate) > 0 {
route.Spec.TLS.ExternalCertificate = &routev1.LocalObjectReference{
Name: o.ExternalCertificate,
}
} else {
cert, err := fileutil.LoadData(o.Cert)
if err != nil {
return err
}
route.Spec.TLS.Certificate = string(cert)
key, err := fileutil.LoadData(o.Key)
if err != nil {
return err
}
route.Spec.TLS.Key = string(key)
}
route.Spec.TLS.Key = string(key)

caCert, err := fileutil.LoadData(o.CACert)
if err != nil {
return err
Expand Down
181 changes: 181 additions & 0 deletions pkg/cli/create/routeedge_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package create

import (
"bytes"
"context"
"os"
"strings"
"testing"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericiooptions"
fakekubernetes "k8s.io/client-go/kubernetes/fake"

routev1 "github.com/openshift/api/route/v1"
routefake "github.com/openshift/client-go/route/clientset/versioned/fake"
)

func writeTestFile(t *testing.T, path, content string) {
t.Helper()
if err := os.WriteFile(path, []byte(content), 0644); err != nil {
t.Fatalf("failed to write test file %s: %v", path, err)
}
}

func newTestService() *corev1.Service {
return &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "my-service",
Namespace: "default",
},
Spec: corev1.ServiceSpec{
Ports: []corev1.ServicePort{
{
Name: "http",
Port: 80,
TargetPort: intstr.FromInt32(8080),
Protocol: corev1.ProtocolTCP,
},
},
},
}
}

func newTestRouteSubcommandOptions(t *testing.T) (*CreateRouteSubcommandOptions, *routefake.Clientset, *bytes.Buffer) {
t.Helper()
service := newTestService()
streams, _, out, _ := genericiooptions.NewTestIOStreams()
fakeKubeClient := fakekubernetes.NewClientset(service)
fakeRouteClientset := routefake.NewClientset()
mapper := meta.NewDefaultRESTMapper(nil)

printFlags := genericclioptions.NewPrintFlags("created").WithTypeSetter(createCmdScheme)
printer, err := printFlags.ToPrinter()
if err != nil {
t.Fatalf("failed to create printer: %v", err)
}

sub := &CreateRouteSubcommandOptions{
PrintFlags: printFlags,
Name: "my-route",
Namespace: "default",
Mapper: mapper,
Client: fakeRouteClientset.RouteV1(),
CoreClient: fakeKubeClient.CoreV1(),
Printer: printer,
IOStreams: streams,
}

return sub, fakeRouteClientset, out
}

func newTestEdgeRouteOptions(t *testing.T) (*CreateEdgeRouteOptions, *routefake.Clientset, *bytes.Buffer) {
t.Helper()
sub, fakeRouteClientset, out := newTestRouteSubcommandOptions(t)
o := &CreateEdgeRouteOptions{
CreateRouteSubcommandOptions: sub,
Service: "my-service",
}
return o, fakeRouteClientset, out
}

func TestCreateEdgeRoute_MutualExclusivity(t *testing.T) {
tests := []struct {
name string
cert string
key string
externalCertificate string
expectError string
}{
{
name: "neither --cert nor --external-certificate set",
},
{
name: "only --cert set",
cert: "/path/to/tls.crt",
},
{
name: "only --external-certificate set",
externalCertificate: "my-secret",
},
{
name: "both --cert and --external-certificate set",
cert: "/path/to/tls.crt",
externalCertificate: "my-secret",
expectError: "--cert and --external-certificate are mutually exclusive",
},
{
name: "both --key and --external-certificate set",
key: "/path/to/tls.key",
externalCertificate: "my-secret",
expectError: "--key and --external-certificate are mutually exclusive",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
o, _, _ := newTestEdgeRouteOptions(t)
o.Cert = tt.cert
o.Key = tt.key
o.ExternalCertificate = tt.externalCertificate

err := o.Validate()
if tt.expectError != "" {
if err == nil {
t.Fatal("expected error but got nil")
}
if !strings.Contains(err.Error(), tt.expectError) {
t.Fatalf("expected error containing %q, got: %v", tt.expectError, err)
}
return
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
})
}
}

func TestCreateEdgeRoute_ExternalCertificateWiring(t *testing.T) {
o, fakeRouteClientset, _ := newTestEdgeRouteOptions(t)
o.ExternalCertificate = "my-cert-secret"

if err := o.Run(); err != nil {
t.Fatalf("unexpected error: %v", err)
}

routes, err := fakeRouteClientset.RouteV1().Routes("default").List(context.TODO(), metav1.ListOptions{})
if err != nil {
t.Fatalf("failed to list routes: %v", err)
}
if len(routes.Items) != 1 {
t.Fatalf("expected 1 route, got %d", len(routes.Items))
}

route := routes.Items[0]
if route.Name != "my-route" {
t.Errorf("expected route name %q, got %q", "my-route", route.Name)
}
if route.Spec.TLS == nil {
t.Fatal("expected TLS config to be set")
}
if route.Spec.TLS.ExternalCertificate == nil {
t.Fatal("expected ExternalCertificate to be set")
}
if route.Spec.TLS.ExternalCertificate.Name != "my-cert-secret" {
t.Errorf("expected ExternalCertificate.Name %q, got %q", "my-cert-secret", route.Spec.TLS.ExternalCertificate.Name)
}
if route.Spec.TLS.Certificate != "" {
t.Errorf("expected no inline certificate, got %q", route.Spec.TLS.Certificate)
}
if route.Spec.TLS.Key != "" {
t.Errorf("expected no inline key, got %q", route.Spec.TLS.Key)
}
if route.Spec.TLS.Termination != routev1.TLSTerminationEdge {
t.Errorf("expected termination %q, got %q", routev1.TLSTerminationEdge, route.Spec.TLS.Termination)
}
}

62 changes: 43 additions & 19 deletions pkg/cli/create/routereenecrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package create

import (
"context"
"fmt"

"github.com/spf13/cobra"

Expand Down Expand Up @@ -34,22 +35,26 @@ var (
# route name default to the service name and the destination CA certificate
# default to the service CA
oc create route reencrypt --service=frontend

# Create a reencrypt route that uses an external certificate from a secret
oc create route reencrypt --service=frontend --external-certificate=my-cert-secret --dest-ca-cert cert.cert
`)
)

type CreateReencryptRouteOptions struct {
CreateRouteSubcommandOptions *CreateRouteSubcommandOptions

Hostname string
Port string
InsecurePolicy string
Service string
Path string
Cert string
Key string
CACert string
DestCACert string
WildcardPolicy string
Hostname string
Port string
InsecurePolicy string
Service string
Path string
Cert string
Key string
CACert string
DestCACert string
ExternalCertificate string
WildcardPolicy string
}

// NewCmdCreateReencryptRoute is a macro command to create a reencrypt route.
Expand All @@ -64,6 +69,7 @@ func NewCmdCreateReencryptRoute(f kcmdutil.Factory, streams genericiooptions.IOS
Example: reencryptRouteExample,
Run: func(cmd *cobra.Command, args []string) {
kcmdutil.CheckErr(o.Complete(f, cmd, args))
kcmdutil.CheckErr(o.Validate())
kcmdutil.CheckErr(o.Run())
},
}
Expand All @@ -82,6 +88,7 @@ func NewCmdCreateReencryptRoute(f kcmdutil.Factory, streams genericiooptions.IOS
cmd.MarkFlagFilename("ca-cert")
cmd.Flags().StringVar(&o.DestCACert, "dest-ca-cert", o.DestCACert, "Path to a CA certificate file, used for securing the connection from the router to the destination. Defaults to the Service CA.")
cmd.MarkFlagFilename("dest-ca-cert")
cmd.Flags().StringVar(&o.ExternalCertificate, "external-certificate", o.ExternalCertificate, "Name of a secret that contains the TLS certificate and key. The secret must contain keys named tls.crt and tls.key. Mutually exclusive with --cert and --key.")
cmd.Flags().StringVar(&o.WildcardPolicy, "wildcard-policy", o.WildcardPolicy, "Sets the WilcardPolicy for the hostname, the default is \"None\". valid values are \"None\" and \"Subdomain\"")

kcmdutil.AddValidateFlags(cmd)
Expand All @@ -95,6 +102,16 @@ func (o *CreateReencryptRouteOptions) Complete(f kcmdutil.Factory, cmd *cobra.Co
return o.CreateRouteSubcommandOptions.Complete(f, cmd, args)
}

func (o *CreateReencryptRouteOptions) Validate() error {
if len(o.Cert) > 0 && len(o.ExternalCertificate) > 0 {
return fmt.Errorf("--cert and --external-certificate are mutually exclusive")
}
if len(o.Key) > 0 && len(o.ExternalCertificate) > 0 {
return fmt.Errorf("--key and --external-certificate are mutually exclusive")
}
return nil
}

func (o *CreateReencryptRouteOptions) Run() error {
serviceName, err := resolveServiceName(o.CreateRouteSubcommandOptions.Mapper, o.Service)
if err != nil {
Expand All @@ -121,16 +138,23 @@ func (o *CreateReencryptRouteOptions) Run() error {
route.Spec.TLS = new(routev1.TLSConfig)
route.Spec.TLS.Termination = routev1.TLSTerminationReencrypt

cert, err := fileutil.LoadData(o.Cert)
if err != nil {
return err
}
route.Spec.TLS.Certificate = string(cert)
key, err := fileutil.LoadData(o.Key)
if err != nil {
return err
if len(o.ExternalCertificate) > 0 {
route.Spec.TLS.ExternalCertificate = &routev1.LocalObjectReference{
Name: o.ExternalCertificate,
}
} else {
cert, err := fileutil.LoadData(o.Cert)
if err != nil {
return err
}
route.Spec.TLS.Certificate = string(cert)
key, err := fileutil.LoadData(o.Key)
if err != nil {
return err
}
route.Spec.TLS.Key = string(key)
}
route.Spec.TLS.Key = string(key)

caCert, err := fileutil.LoadData(o.CACert)
if err != nil {
return err
Expand Down
Loading