Skip to content

Commit 9497bfa

Browse files
committed
Add finalizer logic and condition to Channel resrouce
1 parent 5185648 commit 9497bfa

File tree

6 files changed

+144
-49
lines changed

6 files changed

+144
-49
lines changed

api/v1alpha1/channel_types.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package v1alpha1
1818

1919
import (
20+
"github.com/operator-framework/operator-sdk/pkg/status"
2021
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2122
)
2223

@@ -43,8 +44,8 @@ type ChannelStatus struct {
4344
// ID of the slack channel
4445
ID string `json:"id"`
4546

46-
// Error message if any error has occurred
47-
Error string `json:"error,omitempty"`
47+
// Status conditions
48+
Conditions status.Conditions `json:"conditions,omitempty"`
4849
}
4950

5051
// +kubebuilder:object:root=true
@@ -71,3 +72,13 @@ type ChannelList struct {
7172
func init() {
7273
SchemeBuilder.Register(&Channel{}, &ChannelList{})
7374
}
75+
76+
// GetReconcileStatus - returns conditions, required for making Channel ConditionsStatusAware
77+
func (channel *Channel) GetReconcileStatus() status.Conditions {
78+
return channel.Status.Conditions
79+
}
80+
81+
// SetReconcileStatus - sets status, required for making Channel ConditionsStatusAware
82+
func (channel *Channel) SetReconcileStatus(reconcileStatus status.Conditions) {
83+
channel.Status.Conditions = reconcileStatus
84+
}

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 9 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/slack.stakater.com_channels.yaml

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,46 @@ spec:
5959
status:
6060
description: ChannelStatus defines the observed state of Channel
6161
properties:
62-
error:
63-
description: Error message if any error has occurred
64-
type: string
62+
conditions:
63+
description: Status conditions
64+
items:
65+
description: "Condition represents an observation of an object's state.
66+
Conditions are an extension mechanism intended to be used when the
67+
details of an observation are not a priori known or would not apply
68+
to all instances of a given Kind. \n Conditions should be added
69+
to explicitly convey properties that users and components care about
70+
rather than requiring those properties to be inferred from other
71+
observations. Once defined, the meaning of a Condition can not be
72+
changed arbitrarily - it becomes part of the API, and has the same
73+
backwards- and forwards-compatibility concerns of any other part
74+
of the API."
75+
properties:
76+
lastTransitionTime:
77+
format: date-time
78+
type: string
79+
message:
80+
type: string
81+
reason:
82+
description: ConditionReason is intended to be a one-word, CamelCase
83+
representation of the category of cause of the current status.
84+
It is intended to be used in concise output, such as one-line
85+
kubectl get output, and in summarizing occurrences of causes.
86+
type: string
87+
status:
88+
type: string
89+
type:
90+
description: "ConditionType is the type of the condition and is
91+
typically a CamelCased word or short phrase. \n Condition types
92+
should indicate state in the \"abnormal-true\" polarity. For
93+
example, if the condition indicates when a policy is invalid,
94+
the \"is valid\" case is probably the norm, so the condition
95+
should be called \"Invalid\"."
96+
type: string
97+
required:
98+
- status
99+
- type
100+
type: object
101+
type: array
65102
id:
66103
description: ID of the slack channel
67104
type: string

controllers/channel_controller.go

Lines changed: 59 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,16 @@ import (
2525
ctrl "sigs.k8s.io/controller-runtime"
2626
"sigs.k8s.io/controller-runtime/pkg/client"
2727

28+
finalizerUtil "github.com/stakater/operator-utils/util/finalizer"
29+
reconcilerUtil "github.com/stakater/operator-utils/util/reconciler"
2830
slackv1alpha1 "github.com/stakater/slack-operator/api/v1alpha1"
2931
slack "github.com/stakater/slack-operator/pkg/slack"
3032
)
3133

34+
var (
35+
channelFinalizer string = "slack.stakater.com/channel"
36+
)
37+
3238
// ChannelReconciler reconciles a Channel object
3339
type ChannelReconciler struct {
3440
client.Client
@@ -53,13 +59,34 @@ func (r *ChannelReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
5359
// Request object not found, could have been deleted after reconcile request.
5460
// Owned objects are automatically garbage collected. For additional cleanup logic use finalizers.
5561
// Return and don't requeue
56-
log.Info("Channel resource not found. Deleting channel")
5762
return ctrl.Result{}, nil
5863
}
5964
// Error reading channel, requeue
6065
return ctrl.Result{}, err
6166
}
6267

68+
isChannelMarkedToBeDeleted := channel.GetDeletionTimestamp() != nil
69+
if isChannelMarkedToBeDeleted {
70+
log.Info("Deletion timestamp found for channel " + req.Name)
71+
if finalizerUtil.HasFinalizer(channel, channelFinalizer) {
72+
return r.finalizeChannel(req, channel)
73+
}
74+
// Finalizer doesn't exist so clean up is already done
75+
return reconcilerUtil.DoNotRequeue()
76+
}
77+
78+
// Add finalizer if it doesn't exist
79+
if !finalizerUtil.HasFinalizer(channel, channelFinalizer) {
80+
log.Info("Adding finalizer for channel " + req.Name)
81+
82+
finalizerUtil.AddFinalizer(channel, channelFinalizer)
83+
84+
err := r.Client.Update(ctx, channel)
85+
if err != nil {
86+
return reconcilerUtil.ManageError(r.Client, channel, err, true)
87+
}
88+
}
89+
6390
if channel.Status.ID == "" {
6491
name := channel.Spec.Name
6592
isPrivate := channel.Spec.Private
@@ -69,10 +96,9 @@ func (r *ChannelReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
6996
channelID, err := r.SlackService.CreateChannel(name, isPrivate)
7097

7198
if err != nil {
72-
// Set error state and don't requeue
73-
channel.Status.Error = err.Error()
74-
return ctrl.Result{}, nil
99+
return reconcilerUtil.ManageError(r.Client, channel, err, false)
75100
}
101+
76102
channel.Status.ID = *channelID
77103

78104
err = r.Status().Update(ctx, channel)
@@ -86,7 +112,6 @@ func (r *ChannelReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
86112
return r.updateSlackChannel(ctx, channel)
87113
}
88114

89-
// TODO: too verbose code for error checking
90115
func (r *ChannelReconciler) updateSlackChannel(ctx context.Context, channel *slackv1alpha1.Channel) (ctrl.Result, error) {
91116
channelID := channel.Status.ID
92117
log := r.Log.WithValues("channelID", channelID)
@@ -101,58 +126,55 @@ func (r *ChannelReconciler) updateSlackChannel(ctx context.Context, channel *sla
101126
_, err := r.SlackService.RenameChannel(channelID, name)
102127
if err != nil {
103128
log.Error(err, "Error renaming channel")
104-
channel.Status.Error = err.Error()
105-
106-
err = r.Status().Update(ctx, channel)
107-
if err != nil {
108-
log.Error(err, "Failed to update Channel status")
109-
return ctrl.Result{}, err
110-
}
111-
return ctrl.Result{}, nil
129+
return reconcilerUtil.ManageError(r.Client, channel, err, false)
112130
}
113131

114132
_, err = r.SlackService.SetTopic(channelID, topic)
115133
if err != nil {
116134
log.Error(err, "Error setting channel topic")
117-
channel.Status.Error = err.Error()
118-
119-
err = r.Status().Update(ctx, channel)
120-
if err != nil {
121-
log.Error(err, "Failed to update Channel status")
122-
return ctrl.Result{}, err
123-
}
124-
return ctrl.Result{}, nil
135+
return reconcilerUtil.ManageError(r.Client, channel, err, false)
125136
}
126137

127138
_, err = r.SlackService.SetDescription(channelID, description)
128139
if err != nil {
129140
log.Error(err, "Error setting channel description")
130-
channel.Status.Error = err.Error()
131-
132-
err = r.Status().Update(ctx, channel)
133-
if err != nil {
134-
log.Error(err, "Failed to update Channel status")
135-
return ctrl.Result{}, err
136-
}
137-
return ctrl.Result{}, nil
141+
return reconcilerUtil.ManageError(r.Client, channel, err, false)
138142
}
139143

140144
err = r.SlackService.InviteUsers(channelID, users)
141145
if err != nil {
142146
log.Error(err, "Error inviting users to channel")
143-
channel.Status.Error = err.Error()
144-
145-
err = r.Status().Update(ctx, channel)
146-
if err != nil {
147-
log.Error(err, "Failed to update Channel status")
148-
return ctrl.Result{}, err
149-
}
150-
return ctrl.Result{}, nil
147+
return reconcilerUtil.ManageError(r.Client, channel, err, false)
151148
}
152149

153150
return ctrl.Result{}, nil
154151
}
155152

153+
func (r *ChannelReconciler) finalizeChannel(req ctrl.Request, channel *slackv1alpha1.Channel) (ctrl.Result, error) {
154+
channelID := channel.Status.ID
155+
log := r.Log.WithValues("channelID", channelID)
156+
157+
if channel == nil {
158+
return reconcilerUtil.DoNotRequeue()
159+
}
160+
161+
err := r.SlackService.ArchiveChannel(channelID)
162+
163+
if err != nil && err.Error() != "channel_not_found" {
164+
return reconcilerUtil.ManageError(r.Client, channel, err, false)
165+
}
166+
167+
finalizerUtil.DeleteFinalizer(channel, channelFinalizer)
168+
log.V(1).Info("Finalizer removed for channel")
169+
170+
err = r.Client.Update(context.Background(), channel)
171+
if err != nil {
172+
return reconcilerUtil.ManageError(r.Client, channel, err, false)
173+
}
174+
175+
return reconcilerUtil.DoNotRequeue()
176+
}
177+
156178
// SetupWithManager - Controller-Manager binding configuration
157179
func (r *ChannelReconciler) SetupWithManager(mgr ctrl.Manager) error {
158180
return ctrl.NewControllerManagedBy(mgr).

controllers/channel_controller_test.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ var _ = Describe("ChannelController", func() {
2222
_ = util.CreateChannel(channelName, false, "", "", []string{}, ns)
2323
channel := util.GetChannel(channelName, ns)
2424

25-
Expect(channel.Status.Error).To(BeEmpty())
25+
//Todo: after util
26+
//Expect(channel.Status.Conditions.GetCondition("ReconcileError")).To(BeEmpty())
2627
Expect(channel.Status.ID).To(Equal(slackMock.PublicConversationID))
2728
})
2829
})
@@ -32,7 +33,7 @@ var _ = Describe("ChannelController", func() {
3233
_ = util.CreateChannel(channelName, true, "", "", []string{}, ns)
3334
channel := util.GetChannel(channelName, ns)
3435

35-
Expect(channel.Status.Error).To(BeEmpty())
36+
//Expect(channel.Status.Error).To(BeEmpty())
3637
Expect(channel.Status.ID).To(Equal(slackMock.PrivateConversationID))
3738
})
3839
})
@@ -44,7 +45,7 @@ var _ = Describe("ChannelController", func() {
4445
_ = util.CreateChannel(channelName, true, "", description, []string{}, ns)
4546
channel := util.GetChannel(channelName, ns)
4647

47-
Expect(channel.Status.Error).To(BeEmpty())
48+
//Expect(channel.Status.Error).To(BeEmpty())
4849
Expect(channel.Spec.Description).To(Equal(description))
4950
})
5051
})
@@ -56,7 +57,7 @@ var _ = Describe("ChannelController", func() {
5657
_ = util.CreateChannel(channelName, true, topic, "", []string{}, ns)
5758
channel := util.GetChannel(channelName, ns)
5859

59-
Expect(channel.Status.Error).To(BeEmpty())
60+
//Expect(channel.Status.Error).To(BeEmpty())
6061
Expect(channel.Spec.Topic).To(Equal(topic))
6162
})
6263
})
@@ -84,7 +85,7 @@ var _ = Describe("ChannelController", func() {
8485

8586
updatedChannel := util.GetChannel(channelName, ns)
8687

87-
Expect(updatedChannel.Status.Error).To(BeEmpty())
88+
//Expect(updatedChannel.Status.Error).To(BeEmpty())
8889
Expect(updatedChannel.Spec.Name).To(Equal(newName))
8990
})
9091
})

pkg/slack/service.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ type Service interface {
1212
SetDescription(string, string) (*slack.Channel, error)
1313
SetTopic(string, string) (*slack.Channel, error)
1414
RenameChannel(string, string) (*slack.Channel, error)
15+
ArchiveChannel(string) error
1516
InviteUsers(string, []string) error
1617
}
1718

@@ -110,7 +111,7 @@ func (s *SlackService) RenameChannel(channelID string, newName string) (*slack.C
110111
return channel, nil
111112
}
112113

113-
log.Info("Renaming Slack Channel", "newName", newName)
114+
log.V(1).Info("Renaming Slack Channel", "newName", newName)
114115

115116
channel, err = s.api.RenameConversation(channelID, newName)
116117

@@ -121,6 +122,21 @@ func (s *SlackService) RenameChannel(channelID string, newName string) (*slack.C
121122
return channel, nil
122123
}
123124

125+
// ArchiveChannel archives the slack channel
126+
func (s *SlackService) ArchiveChannel(channelID string) error {
127+
log := s.log.WithValues("channelID", channelID)
128+
129+
log.V(1).Info("Archiving channel")
130+
err := s.api.ArchiveConversation(channelID)
131+
132+
if err != nil {
133+
log.Error(err, "Error archiving channel")
134+
return err
135+
}
136+
137+
return nil
138+
}
139+
124140
// InviteUsers invites users to the slack channel
125141
func (s *SlackService) InviteUsers(channelID string, userEmails []string) error {
126142
log := s.log.WithValues("channelID", channelID)

0 commit comments

Comments
 (0)