Skip to content

Commit

Permalink
Merge pull request #619 from sapcc/audittools-api-update
Browse files Browse the repository at this point in the history
bump go-api-declarations and go-bits, use improved audittools API
  • Loading branch information
SuperSandro2000 authored Dec 6, 2024
2 parents 6746fd8 + b86e865 commit bfc5a48
Show file tree
Hide file tree
Showing 14 changed files with 195 additions and 261 deletions.
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ require (
github.com/prometheus/client_golang v1.20.5
github.com/prometheus/common v0.61.0
github.com/rs/cors v1.11.1
github.com/sapcc/go-api-declarations v1.13.0
github.com/sapcc/go-bits v0.0.0-20241205120930-d112494283ba
github.com/sapcc/go-api-declarations v1.13.1
github.com/sapcc/go-bits v0.0.0-20241206132118-f18a227dc952
go.uber.org/automaxprocs v1.6.0
gopkg.in/yaml.v2 v2.4.0
)
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,10 @@ github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/sapcc/go-api-declarations v1.13.0 h1:4ufQUF7rwhLz7kPDVFCkw6CpQ8VeO2clJg4pjwTTpTU=
github.com/sapcc/go-api-declarations v1.13.0/go.mod h1:83R3hTANhuRXt/pXDby37IJetw8l7DG41s33Tp9NXxI=
github.com/sapcc/go-bits v0.0.0-20241205120930-d112494283ba h1:IRaPAF/j4RcglojJSycoD/Tz9gpVn941VXNm5ZnoAw4=
github.com/sapcc/go-bits v0.0.0-20241205120930-d112494283ba/go.mod h1:ROdTmzQj/gn6dUaxhrCQCTsZtaFJPAFy3CeTD2m/z3k=
github.com/sapcc/go-api-declarations v1.13.1 h1:rovCnLscnoZaIZPWhohSYHzwwYjOnCPsRw3zwtu4tLI=
github.com/sapcc/go-api-declarations v1.13.1/go.mod h1:83R3hTANhuRXt/pXDby37IJetw8l7DG41s33Tp9NXxI=
github.com/sapcc/go-bits v0.0.0-20241206132118-f18a227dc952 h1:tg1xF/eh6kM3Ti5AmMCV6zNKRPHtmP8Yh1XYG0/WbSk=
github.com/sapcc/go-bits v0.0.0-20241206132118-f18a227dc952/go.mod h1:ROdTmzQj/gn6dUaxhrCQCTsZtaFJPAFy3CeTD2m/z3k=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
Expand Down
142 changes: 31 additions & 111 deletions internal/api/audit.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@
package api

import (
"encoding/json"
"fmt"
"strconv"

"github.com/sapcc/go-api-declarations/cadf"
"github.com/sapcc/go-api-declarations/limes"
limesrates "github.com/sapcc/go-api-declarations/limes/rates"
limesresources "github.com/sapcc/go-api-declarations/limes/resources"
"github.com/sapcc/go-bits/must"
)

// maxQuotaEventTarget renders a cadf.Event.Target for a max_quota change event.
Expand All @@ -42,50 +42,48 @@ type maxQuotaEventTarget struct {
}

type maxQuotaChange struct {
OldValue *uint64
NewValue *uint64
OldValue *uint64 `json:"oldMaxQuota"`
NewValue *uint64 `json:"newMaxQuota"`
}

// Render implements the audittools.TargetRenderer interface type.
// Render implements the audittools.Target interface.
func (t maxQuotaEventTarget) Render() cadf.Resource {
payloadBytes, _ := json.Marshal(map[string]any{ //nolint:errcheck // cannot fail because all types are safe to marshal
"oldMaxQuota": t.RequestedChange.OldValue,
"newMaxQuota": t.RequestedChange.NewValue,
})

return cadf.Resource{
TypeURI: fmt.Sprintf("service/%s/%s/max-quota", t.ServiceType, t.ResourceName),
ID: t.ProjectID,
DomainID: t.DomainID,
DomainName: t.DomainName,
ProjectID: t.ProjectID,
ProjectName: t.ProjectName,
Attachments: []cadf.Attachment{{
Name: "payload",
TypeURI: "mime:application/json",
Content: string(payloadBytes),
}},
Attachments: []cadf.Attachment{
must.Return(cadf.NewJSONAttachment("payload", t.RequestedChange)),
},
}
}

// rateLimitEventTarget contains the structure for rendering a cadf.Event.Target for
// changes regarding rate limits
type rateLimitEventTarget struct {
DomainID string
DomainName string
ProjectID string
ProjectName string
ServiceType limes.ServiceType
Name limesrates.RateName
Unit limes.Unit
OldLimit uint64
NewLimit uint64
OldWindow limesrates.Window
NewWindow limesrates.Window
RejectReason string
DomainID string
DomainName string
ProjectID string
ProjectName string
ServiceType limes.ServiceType
Name limesrates.RateName
Payload rateLimitChange
}

// rateLimitChange appears in type rateLimitEventTarget.
type rateLimitChange struct {
Unit limes.Unit `json:"unit,omitempty"`
OldLimit uint64 `json:"oldLimit"`
NewLimit uint64 `json:"newLimit"`
OldWindow limesrates.Window `json:"oldWindow"`
NewWindow limesrates.Window `json:"newWindow"`
RejectReason string `json:"rejectReason,omitempty"`
}

// Render implements the audittools.TargetRenderer interface type.
// Render implements the audittools.Target interface.
func (t rateLimitEventTarget) Render() cadf.Resource {
return cadf.Resource{
TypeURI: fmt.Sprintf("service/%s/%s/rates", t.ServiceType, t.Name),
Expand All @@ -95,18 +93,7 @@ func (t rateLimitEventTarget) Render() cadf.Resource {
ProjectID: t.ProjectID,
ProjectName: t.ProjectName,
Attachments: []cadf.Attachment{
{
Name: "payload",
TypeURI: "mime:application/json",
Content: targetAttachmentContent{
Unit: t.Unit,
OldLimit: t.OldLimit,
NewLimit: t.NewLimit,
OldWindow: t.OldWindow,
NewWindow: t.NewWindow,
RejectReason: t.RejectReason,
},
},
must.Return(cadf.NewJSONAttachment("payload", t.Payload)),
},
}
}
Expand All @@ -122,7 +109,7 @@ type commitmentEventTarget struct {
Commitments []limesresources.Commitment // must have at least one entry
}

// Render implements the audittools.TargetRenderer interface type.
// Render implements the audittools.Target interface.
func (t commitmentEventTarget) Render() cadf.Resource {
if len(t.Commitments) == 0 {
panic("commitmentEventTarget must contain at least one commitment")
Expand All @@ -141,79 +128,12 @@ func (t commitmentEventTarget) Render() cadf.Resource {
if idx > 0 {
name = "additional-payload"
}
res.Attachments = append(res.Attachments, cadf.Attachment{
Name: name,
TypeURI: "mime:application/json",
Content: wrappedAttachment[limesresources.Commitment]{commitment},
})
attachment := must.Return(cadf.NewJSONAttachment(name, commitment))
res.Attachments = append(res.Attachments, attachment)
}
if t.SupersededCommitment != nil {
res.Attachments = append(res.Attachments, cadf.Attachment{
Name: "superseded-payload",
TypeURI: "mime:application/json",
Content: wrappedAttachment[limesresources.Commitment]{*t.SupersededCommitment},
})
attachment := must.Return(cadf.NewJSONAttachment("superseded-payload", *t.SupersededCommitment))
res.Attachments = append(res.Attachments, attachment)
}
return res
}

// This type marshals to JSON like a string containing the JSON representation of its inner type.
// This is the type of structure that cadf.Attachment.Content expects.
type wrappedAttachment[T any] struct {
Inner T
}

// MarshalJSON implements the json.Marshaler interface.
func (a wrappedAttachment[T]) MarshalJSON() ([]byte, error) {
buf, err := json.Marshal(a.Inner)
if err != nil {
return nil, err
}
return json.Marshal(string(buf))
}

// This type is needed for the custom MarshalJSON behavior.
type targetAttachmentContent struct {
RejectReason string
// for quota or rate limit changes
Unit limes.Unit
// for quota changes
OldQuota uint64
NewQuota uint64
// for rate limit changes
OldLimit uint64
NewLimit uint64
OldWindow limesrates.Window
NewWindow limesrates.Window
}

// MarshalJSON implements the json.Marshaler interface.
func (a targetAttachmentContent) MarshalJSON() ([]byte, error) {
// copy data into a struct that does not have a custom MarshalJSON
data := struct {
OldQuota uint64 `json:"oldQuota,omitempty"`
NewQuota uint64 `json:"newQuota,omitempty"`
Unit limes.Unit `json:"unit,omitempty"`
RejectReason string `json:"rejectReason,omitempty"`
OldLimit uint64 `json:"oldLimit,omitempty"`
NewLimit uint64 `json:"newLimit,omitempty"`
OldWindow limesrates.Window `json:"oldWindow,omitempty"`
NewWindow limesrates.Window `json:"newWindow,omitempty"`
}{
OldQuota: a.OldQuota,
NewQuota: a.NewQuota,
Unit: a.Unit,
RejectReason: a.RejectReason,
OldLimit: a.OldLimit,
NewLimit: a.NewLimit,
OldWindow: a.OldWindow,
NewWindow: a.NewWindow,
}
// Hermes does not accept a JSON object at target.attachments[].content, so
// we need to wrap the marshaled JSON into a JSON string
bytes, err := json.Marshal(data)
if err != nil {
return nil, err
}
return json.Marshal(string(bytes))
}
12 changes: 6 additions & 6 deletions internal/api/commitment.go
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ func (p *v1Provider) CreateProjectCommitment(w http.ResponseWriter, r *http.Requ
if respondwith.ErrorText(w, err) {
return
}
p.auditor.Record(audittools.EventParameters{
p.auditor.Record(audittools.Event{
Time: now,
Request: r,
User: token,
Expand Down Expand Up @@ -496,7 +496,7 @@ func (p *v1Provider) DeleteProjectCommitment(w http.ResponseWriter, r *http.Requ
if respondwith.ErrorText(w, err) {
return
}
p.auditor.Record(audittools.EventParameters{
p.auditor.Record(audittools.Event{
Time: p.timeNow(),
Request: r,
User: token,
Expand Down Expand Up @@ -640,7 +640,7 @@ func (p *v1Provider) StartCommitmentTransfer(w http.ResponseWriter, r *http.Requ
}

c := p.convertCommitmentToDisplayForm(dbCommitment, loc, token)
p.auditor.Record(audittools.EventParameters{
p.auditor.Record(audittools.Event{
Time: p.timeNow(),
Request: r,
User: token,
Expand Down Expand Up @@ -802,7 +802,7 @@ func (p *v1Provider) TransferCommitment(w http.ResponseWriter, r *http.Request)
}

c := p.convertCommitmentToDisplayForm(dbCommitment, loc, token)
p.auditor.Record(audittools.EventParameters{
p.auditor.Record(audittools.Event{
Time: p.timeNow(),
Request: r,
User: token,
Expand Down Expand Up @@ -1064,7 +1064,7 @@ func (p *v1Provider) ConvertCommitment(w http.ResponseWriter, r *http.Request) {

c := p.convertCommitmentToDisplayForm(convertedCommitment, targetLoc, token)
auditEvent.Commitments = append([]limesresources.Commitment{c}, auditEvent.Commitments...)
p.auditor.Record(audittools.EventParameters{
p.auditor.Record(audittools.Event{
Time: p.timeNow(),
Request: r,
User: token,
Expand Down Expand Up @@ -1164,7 +1164,7 @@ func (p *v1Provider) UpdateCommitmentDuration(w http.ResponseWriter, r *http.Req
}

c := p.convertCommitmentToDisplayForm(dbCommitment, loc, token)
p.auditor.Record(audittools.EventParameters{
p.auditor.Record(audittools.Event{
Time: p.timeNow(),
Request: r,
User: token,
Expand Down
2 changes: 1 addition & 1 deletion internal/api/projects.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ func (p *v1Provider) PutProjectMaxQuota(w http.ResponseWriter, r *http.Request)
for dbResourceName, requestedChange := range requestedInService {
apiServiceType, apiResourceName, exists := nm.MapToV1API(dbServiceType, dbResourceName)
if exists {
p.auditor.Record(audittools.EventParameters{
p.auditor.Record(audittools.Event{
Time: requestTime,
Request: r,
User: token,
Expand Down
28 changes: 15 additions & 13 deletions internal/api/rate_updater.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,25 +272,27 @@ func (u RateLimitUpdater) CommitAuditTrail(token *gopherpolicy.Token, r *http.Re
}

apiIdentity := u.Cluster.BehaviorForRate(dbServiceType, dbRateName).IdentityInV1API
u.Auditor.Record(audittools.EventParameters{
u.Auditor.Record(audittools.Event{
Time: requestTime,
Request: r,
User: token,
ReasonCode: statusCode,
Action: cadf.UpdateAction,
Target: rateLimitEventTarget{
DomainID: u.Domain.UUID,
DomainName: u.Domain.Name,
ProjectID: u.Project.UUID,
ProjectName: u.Project.Name,
ServiceType: apiIdentity.ServiceType,
Name: apiIdentity.Name,
OldLimit: req.OldLimit,
NewLimit: req.NewLimit,
OldWindow: req.OldWindow,
NewWindow: req.NewWindow,
Unit: req.Unit,
RejectReason: rejectReason,
DomainID: u.Domain.UUID,
DomainName: u.Domain.Name,
ProjectID: u.Project.UUID,
ProjectName: u.Project.Name,
ServiceType: apiIdentity.ServiceType,
Name: apiIdentity.Name,
Payload: rateLimitChange{
OldLimit: req.OldLimit,
NewLimit: req.NewLimit,
OldWindow: req.OldWindow,
NewWindow: req.NewWindow,
Unit: req.Unit,
RejectReason: rejectReason,
},
},
})
}
Expand Down
29 changes: 29 additions & 0 deletions vendor/github.com/sapcc/go-api-declarations/cadf/event.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit bfc5a48

Please sign in to comment.