This repository has been archived by the owner on Nov 27, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
rest: add persistence/writeclient (#132)
* rest: add persistence/writeclient Signed-off-by: Francesco Ilario <filario@redhat.com> * add more tests, refactor Signed-off-by: Francesco Ilario <filario@redhat.com> * add comments and rename BuildClientFunc constructor func Signed-off-by: Francesco Ilario <filario@redhat.com> * update tests Signed-off-by: Francesco Ilario <filario@redhat.com> --------- Signed-off-by: Francesco Ilario <filario@redhat.com>
- Loading branch information
Showing
7 changed files
with
434 additions
and
0 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,19 @@ | ||
package workspace | ||
|
||
import ( | ||
"context" | ||
|
||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
|
||
restworkspacesv1alpha1 "github.com/konflux-workspaces/workspaces/server/api/v1alpha1" | ||
) | ||
|
||
// WorkspaceCreator is the interface the data source needs to implement to allow the CreateWorkspaceHandler to properly create the workspace | ||
type WorkspaceCreator interface { | ||
CreateUserWorkspace(ctx context.Context, user string, workspace *restworkspacesv1alpha1.Workspace, opts ...client.CreateOption) error | ||
} | ||
|
||
// WorkspaceUpdater is the interface the data source needs to implement to allow the UpdateWorkspaceHandler to update the workspace | ||
type WorkspaceUpdater interface { | ||
UpdateUserWorkspace(ctx context.Context, user string, obj *restworkspacesv1alpha1.Workspace, opts ...client.UpdateOption) error | ||
} |
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,53 @@ | ||
package writeclient | ||
|
||
import ( | ||
"k8s.io/apimachinery/pkg/runtime" | ||
"k8s.io/client-go/rest" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
|
||
"github.com/konflux-workspaces/workspaces/server/persistence/iwclient" | ||
|
||
workspacesv1alpha1 "github.com/konflux-workspaces/workspaces/operator/api/v1alpha1" | ||
restworkspacesv1alpha1 "github.com/konflux-workspaces/workspaces/server/api/v1alpha1" | ||
) | ||
|
||
// BuildClientFunc defines a function that builds a controller-runtime client | ||
// that impersonates the given user | ||
type BuildClientFunc func(user string) (client.Client, error) | ||
|
||
// WriteClient implements Write primitives on Workspaces. | ||
// Creates or updates InternalWorkspaces starting from a request on Workspaces. | ||
type WriteClient struct { | ||
buildClient BuildClientFunc | ||
workspacesNamespace string | ||
workspacesReader *iwclient.Client | ||
} | ||
|
||
// New creates a new WriteClient | ||
func New(buildClient BuildClientFunc, workspacesNamespace string, workspacesReader *iwclient.Client) *WriteClient { | ||
return &WriteClient{ | ||
buildClient: buildClient, | ||
workspacesNamespace: workspacesNamespace, | ||
workspacesReader: workspacesReader, | ||
} | ||
} | ||
|
||
// BuildBuildClientFuncForConfig provides a configured BuildClientFunc for building a controller-runtime client | ||
// for a given cluster and impersonating an user | ||
func BuildBuildClientFuncForConfig(config *rest.Config) BuildClientFunc { | ||
newConfig := rest.CopyConfig(config) | ||
|
||
return func(user string) (client.Client, error) { | ||
newConfig.Impersonate.UserName = user | ||
|
||
s := runtime.NewScheme() | ||
if err := restworkspacesv1alpha1.AddToScheme(s); err != nil { | ||
return nil, err | ||
} | ||
if err := workspacesv1alpha1.AddToScheme(s); err != nil { | ||
return nil, err | ||
} | ||
|
||
return client.New(newConfig, client.Options{Scheme: s}) | ||
} | ||
} |
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,48 @@ | ||
package writeclient | ||
|
||
import ( | ||
"context" | ||
|
||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
|
||
"github.com/konflux-workspaces/workspaces/server/core/workspace/v2" | ||
"github.com/konflux-workspaces/workspaces/server/log" | ||
"github.com/konflux-workspaces/workspaces/server/persistence/mapper" | ||
|
||
restworkspacesv1alpha1 "github.com/konflux-workspaces/workspaces/server/api/v1alpha1" | ||
) | ||
|
||
var _ workspace.WorkspaceCreator = &WriteClient{} | ||
|
||
// CreateUserWorkspace creates as `user` the InternalWorkspace representing the provided Workspace | ||
func (c *WriteClient) CreateUserWorkspace(ctx context.Context, user string, workspace *restworkspacesv1alpha1.Workspace, opts ...client.CreateOption) error { | ||
cli, err := c.buildClient(user) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// map Workspace to InternalWorkspace | ||
iw, err := mapper.Default.WorkspaceToInternalWorkspace(workspace) | ||
if err != nil { | ||
return err | ||
} | ||
iw.SetNamespace(c.workspacesNamespace) | ||
iw.SetName("") | ||
iw.SetGenerateName(workspace.Name) | ||
|
||
// create InternalWorkspace | ||
log.FromContext(ctx).Debug("creating user workspace", "workspace", workspace, "user", user) | ||
if err := cli.Create(ctx, iw, opts...); err != nil { | ||
return err | ||
} | ||
|
||
// map InternalWorkspace to Workspace | ||
w, err := mapper.Default.InternalWorkspaceToWorkspace(iw) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// fill return value | ||
w.DeepCopyInto(workspace) | ||
return nil | ||
} |
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,93 @@ | ||
package writeclient_test | ||
|
||
import ( | ||
"context" | ||
|
||
. "github.com/onsi/ginkgo/v2" | ||
. "github.com/onsi/gomega" | ||
|
||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
"sigs.k8s.io/controller-runtime/pkg/client/fake" | ||
|
||
"github.com/konflux-workspaces/workspaces/server/persistence/iwclient" | ||
"github.com/konflux-workspaces/workspaces/server/persistence/writeclient" | ||
|
||
workspacesv1alpha1 "github.com/konflux-workspaces/workspaces/operator/api/v1alpha1" | ||
restworkspacesv1alpha1 "github.com/konflux-workspaces/workspaces/server/api/v1alpha1" | ||
) | ||
|
||
var _ = Describe("WriteclientCreate", func() { | ||
var ctx context.Context | ||
var fakeClient client.WithWatch | ||
var cli *writeclient.WriteClient | ||
|
||
user := "foo" | ||
namespace := "bar" | ||
workspace := restworkspacesv1alpha1.Workspace{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Namespace: "owner", | ||
Name: "workspace-foo", | ||
}, | ||
Spec: restworkspacesv1alpha1.WorkspaceSpec{}, | ||
} | ||
|
||
validateCreatedInternalWorkspace := func(w *restworkspacesv1alpha1.Workspace, expectedVisibility workspacesv1alpha1.InternalWorkspaceVisibility) { | ||
ww := workspacesv1alpha1.InternalWorkspaceList{} | ||
err := fakeClient.List( | ||
ctx, | ||
&ww, | ||
client.InNamespace(namespace), | ||
client.MatchingLabels{ | ||
workspacesv1alpha1.LabelDisplayName: workspace.Name, | ||
workspacesv1alpha1.LabelWorkspaceOwner: "owner", | ||
}, | ||
) | ||
Expect(err).NotTo(HaveOccurred()) | ||
Expect(ww.Items).To(HaveLen(1)) | ||
Expect(ww.Items[0].Spec).ToNot(BeNil()) | ||
Expect(ww.Items[0].Spec.Visibility).To(Equal(expectedVisibility)) | ||
} | ||
|
||
BeforeEach(func() { | ||
ctx = context.Background() | ||
fakeClient = fake.NewFakeClient() | ||
err := workspacesv1alpha1.AddToScheme(fakeClient.Scheme()) | ||
Expect(err).NotTo(HaveOccurred()) | ||
|
||
clientFunc := func(string) (client.Client, error) { | ||
return fakeClient, nil | ||
} | ||
|
||
iwcli := iwclient.New(fakeClient, namespace, namespace) | ||
cli = writeclient.New(clientFunc, namespace, iwcli) | ||
}) | ||
|
||
When("creating a community workspace", func() { | ||
It("should create workspaces using the given client", func() { | ||
// given | ||
workspace.Spec.Visibility = restworkspacesv1alpha1.WorkspaceVisibilityCommunity | ||
|
||
// when | ||
err := cli.CreateUserWorkspace(ctx, user, &workspace) | ||
|
||
// then | ||
Expect(err).NotTo(HaveOccurred()) | ||
validateCreatedInternalWorkspace(&workspace, workspacesv1alpha1.InternalWorkspaceVisibilityCommunity) | ||
}) | ||
}) | ||
|
||
When("creating a community workspace", func() { | ||
It("should create workspaces using the given client", func() { | ||
// given | ||
workspace.Spec.Visibility = restworkspacesv1alpha1.WorkspaceVisibilityPrivate | ||
|
||
// when | ||
err := cli.CreateUserWorkspace(ctx, user, &workspace) | ||
|
||
// then | ||
Expect(err).NotTo(HaveOccurred()) | ||
validateCreatedInternalWorkspace(&workspace, workspacesv1alpha1.InternalWorkspaceVisibilityPrivate) | ||
}) | ||
}) | ||
}) |
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,18 @@ | ||
package writeclient_test | ||
|
||
import ( | ||
"log/slog" | ||
"testing" | ||
|
||
. "github.com/onsi/ginkgo/v2" | ||
. "github.com/onsi/gomega" | ||
|
||
"github.com/konflux-workspaces/workspaces/server/log" | ||
) | ||
|
||
func TestWriteclient(t *testing.T) { | ||
slog.SetDefault(slog.New(&log.NoOpHandler{})) | ||
|
||
RegisterFailHandler(Fail) | ||
RunSpecs(t, "WriteClient Suite") | ||
} |
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,52 @@ | ||
package writeclient | ||
|
||
import ( | ||
"context" | ||
|
||
kerrors "k8s.io/apimachinery/pkg/api/errors" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
|
||
"github.com/konflux-workspaces/workspaces/server/core/workspace/v2" | ||
"github.com/konflux-workspaces/workspaces/server/log" | ||
"github.com/konflux-workspaces/workspaces/server/persistence/iwclient" | ||
"github.com/konflux-workspaces/workspaces/server/persistence/mapper" | ||
|
||
workspacesv1alpha1 "github.com/konflux-workspaces/workspaces/operator/api/v1alpha1" | ||
restworkspacesv1alpha1 "github.com/konflux-workspaces/workspaces/server/api/v1alpha1" | ||
) | ||
|
||
var _ workspace.WorkspaceUpdater = &WriteClient{} | ||
|
||
// UpdateUserWorkspace updates as `user` the InternalWorkspace representing the provided Workspace | ||
func (c *WriteClient) UpdateUserWorkspace(ctx context.Context, user string, workspace *restworkspacesv1alpha1.Workspace, opts ...client.UpdateOption) error { | ||
// build client impersonating the user | ||
cli, err := c.buildClient(user) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// map to InternalWorkspace | ||
iw, err := mapper.Default.WorkspaceToInternalWorkspace(workspace) | ||
if err != nil { | ||
return kerrors.NewBadRequest("malformed workspace") | ||
} | ||
|
||
// get the InternalWorkspace as user | ||
ciw := workspacesv1alpha1.InternalWorkspace{} | ||
key := iwclient.SpaceKey{Owner: workspace.Namespace, Name: workspace.Name} | ||
if err := c.workspacesReader.GetAsUser(ctx, user, key, &ciw); err != nil { | ||
return kerrors.NewNotFound(restworkspacesv1alpha1.GroupVersion.WithResource("workspaces").GroupResource(), workspace.Name) | ||
} | ||
|
||
// check Generation matching | ||
if iw.Generation != ciw.Generation { | ||
return kerrors.NewResourceExpired("workspace version changed") | ||
} | ||
|
||
// update the InternalWorkspace | ||
iw.SetName(ciw.GetName()) | ||
iw.SetNamespace(ciw.GetNamespace()) | ||
iw.SetResourceVersion(ciw.GetResourceVersion()) | ||
log.FromContext(ctx).Debug("updating user workspace", "workspace", iw, "user", user) | ||
return cli.Update(ctx, iw, opts...) | ||
} |
Oops, something went wrong.