-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
operator: Add OpenShift CloudCredentials support for AWS STS (#11524)
Co-authored-by: Periklis Tsirakidis <[email protected]>
- Loading branch information
1 parent
de4f56e
commit fe4ba0c
Showing
46 changed files
with
1,549 additions
and
119 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
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
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
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
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
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
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
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
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
71 changes: 71 additions & 0 deletions
71
operator/controllers/loki/credentialsrequests_controller.go
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,71 @@ | ||
package controllers | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/go-logr/logr" | ||
apierrors "k8s.io/apimachinery/pkg/api/errors" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
ctrl "sigs.k8s.io/controller-runtime" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
|
||
lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" | ||
"github.com/grafana/loki/operator/controllers/loki/internal/lokistack" | ||
"github.com/grafana/loki/operator/controllers/loki/internal/management/state" | ||
"github.com/grafana/loki/operator/internal/external/k8s" | ||
"github.com/grafana/loki/operator/internal/handlers" | ||
) | ||
|
||
// CredentialsRequestsReconciler reconciles a single CredentialsRequest resource for each LokiStack request. | ||
type CredentialsRequestsReconciler struct { | ||
client.Client | ||
Scheme *runtime.Scheme | ||
Log logr.Logger | ||
} | ||
|
||
// Reconcile creates a single CredentialsRequest per LokiStack for the OpenShift cloud-credentials-operator (CCO) to | ||
// provide a managed cloud credentials Secret. On successful creation, the LokiStack resource is annotated | ||
// with `loki.grafana.com/credentials-request-secret-ref` that refers to the secret provided by CCO. If the LokiStack | ||
// resource is not found its accompanying CredentialsRequest resource is deleted. | ||
func (r *CredentialsRequestsReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { | ||
var stack lokiv1.LokiStack | ||
if err := r.Client.Get(ctx, req.NamespacedName, &stack); err != nil { | ||
if apierrors.IsNotFound(err) { | ||
return ctrl.Result{}, handlers.DeleteCredentialsRequest(ctx, r.Client, req.NamespacedName) | ||
} | ||
return ctrl.Result{}, err | ||
} | ||
|
||
managed, err := state.IsManaged(ctx, req, r.Client) | ||
if err != nil { | ||
return ctrl.Result{}, err | ||
} | ||
if !managed { | ||
r.Log.Info("Skipping reconciliation for unmanaged LokiStack resource", "name", req.String()) | ||
// Stop requeueing for unmanaged LokiStack custom resources | ||
return ctrl.Result{}, nil | ||
} | ||
|
||
secretRef, err := handlers.CreateCredentialsRequest(ctx, r.Client, req.NamespacedName) | ||
if err != nil { | ||
return ctrl.Result{}, err | ||
} | ||
|
||
if err := lokistack.AnnotateForCredentialsRequest(ctx, r.Client, req.NamespacedName, secretRef); err != nil { | ||
return ctrl.Result{}, err | ||
} | ||
|
||
return ctrl.Result{}, nil | ||
} | ||
|
||
// SetupWithManager sets up the controller with the Manager. | ||
func (r *CredentialsRequestsReconciler) SetupWithManager(mgr ctrl.Manager) error { | ||
b := ctrl.NewControllerManagedBy(mgr) | ||
return r.buildController(k8s.NewCtrlBuilder(b)) | ||
} | ||
|
||
func (r *CredentialsRequestsReconciler) buildController(bld k8s.Builder) error { | ||
return bld. | ||
For(&lokiv1.LokiStack{}). | ||
Complete(r) | ||
} |
155 changes: 155 additions & 0 deletions
155
operator/controllers/loki/credentialsrequests_controller_test.go
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,155 @@ | ||
package controllers | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
cloudcredentialsv1 "github.com/openshift/cloud-credential-operator/pkg/apis/cloudcredential/v1" | ||
"github.com/stretchr/testify/require" | ||
apierrors "k8s.io/apimachinery/pkg/api/errors" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/runtime/schema" | ||
"k8s.io/apimachinery/pkg/types" | ||
ctrl "sigs.k8s.io/controller-runtime" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
|
||
lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" | ||
"github.com/grafana/loki/operator/internal/external/k8s/k8sfakes" | ||
"github.com/grafana/loki/operator/internal/manifests/storage" | ||
) | ||
|
||
func TestCredentialsRequestController_RegistersCustomResource_WithDefaultPredicates(t *testing.T) { | ||
b := &k8sfakes.FakeBuilder{} | ||
k := &k8sfakes.FakeClient{} | ||
c := &CredentialsRequestsReconciler{Client: k, Scheme: scheme} | ||
|
||
b.ForReturns(b) | ||
b.OwnsReturns(b) | ||
|
||
err := c.buildController(b) | ||
require.NoError(t, err) | ||
|
||
// Require only one For-Call for the custom resource | ||
require.Equal(t, 1, b.ForCallCount()) | ||
|
||
// Require For-call with LokiStack resource | ||
obj, _ := b.ForArgsForCall(0) | ||
require.Equal(t, &lokiv1.LokiStack{}, obj) | ||
} | ||
|
||
func TestCredentialsRequestController_DeleteCredentialsRequest_WhenLokiStackNotFound(t *testing.T) { | ||
k := &k8sfakes.FakeClient{} | ||
c := &CredentialsRequestsReconciler{Client: k, Scheme: scheme} | ||
r := ctrl.Request{ | ||
NamespacedName: types.NamespacedName{ | ||
Name: "my-stack", | ||
Namespace: "ns", | ||
}, | ||
} | ||
|
||
// Set managed auth environment | ||
t.Setenv("ROLEARN", "a-role-arn") | ||
|
||
k.GetStub = func(_ context.Context, key types.NamespacedName, _ client.Object, _ ...client.GetOption) error { | ||
if key.Name == r.Name && key.Namespace == r.Namespace { | ||
return apierrors.NewNotFound(schema.GroupResource{}, "lokistack not found") | ||
} | ||
return nil | ||
} | ||
|
||
res, err := c.Reconcile(context.Background(), r) | ||
require.NoError(t, err) | ||
require.Equal(t, ctrl.Result{}, res) | ||
require.Equal(t, 1, k.DeleteCallCount()) | ||
} | ||
|
||
func TestCredentialsRequestController_CreateCredentialsRequest_WhenLokiStackNotAnnotated(t *testing.T) { | ||
k := &k8sfakes.FakeClient{} | ||
c := &CredentialsRequestsReconciler{Client: k, Scheme: scheme} | ||
r := ctrl.Request{ | ||
NamespacedName: types.NamespacedName{ | ||
Name: "my-stack", | ||
Namespace: "ns", | ||
}, | ||
} | ||
s := lokiv1.LokiStack{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "my-stack", | ||
Namespace: "ns", | ||
}, | ||
Spec: lokiv1.LokiStackSpec{ | ||
ManagementState: lokiv1.ManagementStateManaged, | ||
}, | ||
} | ||
|
||
// Set managed auth environment | ||
t.Setenv("ROLEARN", "a-role-arn") | ||
|
||
k.GetStub = func(_ context.Context, key types.NamespacedName, out client.Object, _ ...client.GetOption) error { | ||
if key.Name == r.Name && key.Namespace == r.Namespace { | ||
k.SetClientObject(out, &s) | ||
return nil | ||
} | ||
return apierrors.NewNotFound(schema.GroupResource{}, "lokistack not found") | ||
} | ||
|
||
k.CreateStub = func(_ context.Context, o client.Object, _ ...client.CreateOption) error { | ||
_, isCredReq := o.(*cloudcredentialsv1.CredentialsRequest) | ||
if !isCredReq { | ||
return apierrors.NewBadRequest("something went wrong creating a credentials request") | ||
} | ||
return nil | ||
} | ||
|
||
k.UpdateStub = func(_ context.Context, o client.Object, _ ...client.UpdateOption) error { | ||
stack, ok := o.(*lokiv1.LokiStack) | ||
if !ok { | ||
return apierrors.NewBadRequest("something went wrong creating a credentials request") | ||
} | ||
|
||
_, hasSecretRef := stack.Annotations[storage.AnnotationCredentialsRequestsSecretRef] | ||
if !hasSecretRef { | ||
return apierrors.NewBadRequest("something went updating the lokistack annotations") | ||
} | ||
return nil | ||
} | ||
|
||
res, err := c.Reconcile(context.Background(), r) | ||
require.NoError(t, err) | ||
require.Equal(t, ctrl.Result{}, res) | ||
require.Equal(t, 1, k.CreateCallCount()) | ||
require.Equal(t, 1, k.UpdateCallCount()) | ||
} | ||
|
||
func TestCredentialsRequestController_SkipsUnmanaged(t *testing.T) { | ||
k := &k8sfakes.FakeClient{} | ||
c := &CredentialsRequestsReconciler{Client: k, Scheme: scheme} | ||
r := ctrl.Request{ | ||
NamespacedName: types.NamespacedName{ | ||
Name: "my-stack", | ||
Namespace: "ns", | ||
}, | ||
} | ||
|
||
s := lokiv1.LokiStack{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "my-stack", | ||
Namespace: "ns", | ||
}, | ||
Spec: lokiv1.LokiStackSpec{ | ||
ManagementState: lokiv1.ManagementStateUnmanaged, | ||
}, | ||
} | ||
|
||
k.GetStub = func(_ context.Context, key types.NamespacedName, out client.Object, _ ...client.GetOption) error { | ||
if key.Name == s.Name && key.Namespace == s.Namespace { | ||
k.SetClientObject(out, &s) | ||
return nil | ||
} | ||
return apierrors.NewNotFound(schema.GroupResource{}, "something not found") | ||
} | ||
|
||
res, err := c.Reconcile(context.Background(), r) | ||
require.NoError(t, err) | ||
require.Equal(t, ctrl.Result{}, res) | ||
} |
30 changes: 30 additions & 0 deletions
30
operator/controllers/loki/internal/lokistack/credentialsrequest_discovery.go
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,30 @@ | ||
package lokistack | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/ViaQ/logerr/v2/kverrors" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
|
||
"github.com/grafana/loki/operator/internal/external/k8s" | ||
"github.com/grafana/loki/operator/internal/manifests/storage" | ||
) | ||
|
||
// AnnotateForCredentialsRequest adds the `loki.grafana.com/credentials-request-secret-ref` annotation | ||
// to the named Lokistack. If no LokiStack is found, then skip reconciliation. Or else return an error. | ||
func AnnotateForCredentialsRequest(ctx context.Context, k k8s.Client, key client.ObjectKey, secretRef string) error { | ||
stack, err := getLokiStack(ctx, k, key) | ||
if stack == nil || err != nil { | ||
return err | ||
} | ||
|
||
if val, ok := stack.Annotations[storage.AnnotationCredentialsRequestsSecretRef]; ok && val == secretRef { | ||
return nil | ||
} | ||
|
||
if err := updateAnnotation(ctx, k, stack, storage.AnnotationCredentialsRequestsSecretRef, secretRef); err != nil { | ||
return kverrors.Wrap(err, "failed to update lokistack `credentialsRequestSecretRef` annotation", "key", key) | ||
} | ||
|
||
return nil | ||
} |
Oops, something went wrong.