Skip to content

Commit 85c907d

Browse files
authored
Merge pull request #489 from vshn/feature/billing
Event based billing controller
2 parents 54c584e + 50e6051 commit 85c907d

File tree

19 files changed

+1360
-6
lines changed

19 files changed

+1360
-6
lines changed

apis/vshn/v1/billing_service.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package v1
2+
3+
import (
4+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
5+
)
6+
7+
const (
8+
// BillingServiceFinalizer is the finalizer used to protect BillingService resources from deletion
9+
BillingServiceFinalizer = "billing.appcat.vshn.io/delete-protection"
10+
)
11+
12+
// +kubebuilder:object:root=true
13+
// +kubebuilder:subresource:status
14+
// +kubebuilder:object:generate=true
15+
// +kubebuilder:resource:scope=Namespaced,categories=appcat
16+
// +kubebuilder:printcolumn:name="READY",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].status"
17+
// +kubebuilder:printcolumn:name="SYNCED",type="string",JSONPath=".status.conditions[?(@.type=='Synced')].status"
18+
// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp"
19+
20+
// BillingService represents a service instance for billing purposes
21+
type BillingService struct {
22+
metav1.TypeMeta `json:",inline"`
23+
metav1.ObjectMeta `json:"metadata,omitempty"`
24+
25+
// Spec defines the desired state of a BillingService
26+
Spec BillingServiceSpec `json:"spec"`
27+
28+
// Status reflects the observed state of a BillingService
29+
Status BillingServiceStatus `json:"status,omitempty"`
30+
}
31+
32+
// BillingServiceSpec defines the desired state of a BillingService
33+
type BillingServiceSpec struct {
34+
// KeepAfterDeletion defines how many days to keep billing records after service deletion
35+
KeepAfterDeletion int `json:"keepAfterDeletion,omitempty"`
36+
37+
// Odoo contains Odoo-specific billing configuration
38+
Odoo OdooSpec `json:"odoo,omitempty"`
39+
}
40+
41+
// OdooSpec defines Odoo-specific billing configuration
42+
type OdooSpec struct {
43+
// InstanceID uniquely identifies the service instance in Odoo
44+
InstanceID string `json:"instanceID"`
45+
46+
// ProductID identifies the product in the billing system
47+
ProductID string `json:"productID"`
48+
49+
// SalesOrderID identifies the sales order in Odoo
50+
SalesOrderID string `json:"salesOrderID,omitempty"`
51+
52+
// UnitID defines the billing unit type in Odoo
53+
UnitID string `json:"unitID"`
54+
55+
// Size represents the size of the service instance
56+
Size string `json:"size,omitempty"`
57+
58+
// ItemGroupDescription describes the billing item group
59+
ItemGroupDescription string `json:"itemGroupDescription"`
60+
61+
// ItemDescription is a human readable description of the billing item
62+
ItemDescription string `json:"itemDescription"`
63+
}
64+
65+
// BillingServiceStatus defines the observed state of a BillingService
66+
type BillingServiceStatus struct {
67+
// Events contains the history of billing events
68+
Events []BillingEventStatus `json:"events,omitempty"`
69+
70+
// Conditions represent the latest available observations of the billing service's state
71+
Conditions []metav1.Condition `json:"conditions,omitempty"`
72+
}
73+
74+
// BillingEventStatus represents the status of a billing event
75+
type BillingEventStatus struct {
76+
// Type is the type of billing event (created, deleted, scaled)
77+
// +kubebuilder:validation:Enum="created";"deleted";"scaled"
78+
Type string `json:"type"`
79+
80+
// ProductID identifies the product in the billing system
81+
ProductID string `json:"productId"`
82+
83+
// Size represents the size/plan at the time of the event
84+
Size string `json:"size"`
85+
86+
// Timestamp when the event occurred
87+
Timestamp metav1.Time `json:"timestamp"`
88+
89+
// State represents the current state of the event (sent, pending, failed, superseded)
90+
// +kubebuilder:validation:Enum="sent";"pending";"failed";"superseded"
91+
State string `json:"state"`
92+
93+
// RetryCount tracks the number of retry attempts for failed events
94+
// +kubebuilder:default=0
95+
RetryCount int `json:"retryCount,omitempty"`
96+
97+
// LastAttemptTime is when we last tried to send this event
98+
LastAttemptTime metav1.Time `json:"lastAttemptTime,omitempty"`
99+
}
100+
101+
// +kubebuilder:object:root=true
102+
// +kubebuilder:object:generate=true
103+
104+
// BillingServiceList contains a list of BillingService
105+
type BillingServiceList struct {
106+
metav1.TypeMeta `json:",inline"`
107+
metav1.ListMeta `json:"metadata,omitempty"`
108+
Items []BillingService `json:"items"`
109+
}

apis/vshn/v1/groupversion_info.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ var (
2222

2323
func init() {
2424
SchemeBuilder.Register(
25+
&BillingService{},
26+
&BillingServiceList{},
27+
2528
&VSHNPostgreSQL{},
2629
&VSHNPostgreSQLList{},
2730
&XVSHNPostgreSQL{},

apis/vshn/v1/zz_generated.deepcopy.go

Lines changed: 137 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/controller.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ import (
77
"github.com/spf13/cobra"
88
"github.com/spf13/viper"
99
"github.com/vshn/appcat/v4/pkg"
10+
"github.com/vshn/appcat/v4/pkg/controller/billing"
1011
"github.com/vshn/appcat/v4/pkg/controller/events"
1112
"github.com/vshn/appcat/v4/pkg/controller/webhooks"
13+
"github.com/vshn/appcat/v4/pkg/odoo"
1214
"k8s.io/apimachinery/pkg/runtime"
1315
ctrl "sigs.k8s.io/controller-runtime"
1416
"sigs.k8s.io/controller-runtime/pkg/healthz"
@@ -26,6 +28,7 @@ type controller struct {
2628
enableProviderWebhooks bool
2729
enableQuotas bool
2830
enableEventForwarding bool
31+
enableBilling bool
2932
certDir string
3033
}
3134

@@ -51,6 +54,7 @@ func init() {
5154
ControllerCMD.Flags().StringVar(&c.certDir, "certdir", "/etc/webhook/certs", "Set the webhook certificate directory")
5255
ControllerCMD.Flags().BoolVar(&c.enableQuotas, "quotas", false, "Enable the quota webhooks, is only active if webhooks is also true")
5356
ControllerCMD.Flags().BoolVar(&c.enableEventForwarding, "event-forwarding", true, "Disable event-forwarding")
57+
ControllerCMD.Flags().BoolVar(&c.enableBilling, "billing", true, "Disable billing")
5458
viper.AutomaticEnv()
5559
if !viper.IsSet("PLANS_NAMESPACE") {
5660
viper.Set("PLANS_NAMESPACE", "syn-appcat")
@@ -79,6 +83,30 @@ func (c *controller) executeController(cmd *cobra.Command, _ []string) error {
7983
return err
8084
}
8185

86+
if c.enableBilling {
87+
opts := odoo.Options{
88+
BaseURL: viper.GetString("ODOO_BASE_URL"),
89+
DB: viper.GetString("ODOO_DB"),
90+
ClientID: viper.GetString("ODOO_CLIENT_ID"),
91+
ClientSecret: viper.GetString("ODOO_CLIENT_SECRET"),
92+
TokenURL: viper.GetString("ODOO_TOKEN_URL"),
93+
}
94+
95+
if opts.BaseURL == "" || opts.DB == "" || opts.ClientID == "" || opts.ClientSecret == "" || opts.TokenURL == "" {
96+
return fmt.Errorf("billing is enabled but Odoo configuration is incomplete")
97+
}
98+
99+
odooClient, err := odoo.NewClient(cmd.Context(), opts)
100+
if err != nil {
101+
return fmt.Errorf("initialize Odoo client: %w", err)
102+
}
103+
104+
b := billing.New(mgr.GetClient(), mgr.GetScheme(), odooClient)
105+
if err := b.SetupWithManager(mgr); err != nil {
106+
return err
107+
}
108+
}
109+
82110
if c.enableEventForwarding {
83111
events := &events.EventHandler{
84112
Client: mgr.GetClient(),

config/controller/cluster-role.yaml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,32 @@ rules:
8383
- get
8484
- list
8585
- watch
86+
- apiGroups:
87+
- vshn.appcat.vshn.io
88+
resources:
89+
- billingservices
90+
verbs:
91+
- create
92+
- delete
93+
- get
94+
- list
95+
- patch
96+
- update
97+
- watch
98+
- apiGroups:
99+
- vshn.appcat.vshn.io
100+
resources:
101+
- billingservices/finalizers
102+
verbs:
103+
- update
104+
- apiGroups:
105+
- vshn.appcat.vshn.io
106+
resources:
107+
- billingservices/status
108+
verbs:
109+
- get
110+
- patch
111+
- update
86112
- apiGroups:
87113
- vshn.appcat.vshn.io
88114
resources:

0 commit comments

Comments
 (0)