Skip to content

feat: support project-level Mattermost integration (client-go v1)#268

Merged
MisterMX merged 6 commits intocrossplane-contrib:masterfrom
Vadims-U:feature/mm-on-clientgo-v1
Mar 5, 2026
Merged

feat: support project-level Mattermost integration (client-go v1)#268
MisterMX merged 6 commits intocrossplane-contrib:masterfrom
Vadims-U:feature/mm-on-clientgo-v1

Conversation

@Vadims-U
Copy link
Contributor

@Vadims-U Vadims-U commented Jan 8, 2026

This PR completes and modernizes support for project‑level Mattermost integration, originally started in draft PR #261. The draft required updates to work with the new gitlab-org/api/client-go v1 SDK and recent provider changes.

This PR:

  • updates the original implementation to the new API client,
  • fixes type mismatches introduced by the SDK migration (migrated all necessary numeric fields to int64),
  • completes missing functionality,
  • cleans up API types,
  • brings the resource to a fully working, test‑verified state.

I recommend a squash merge to keep history clean.

Refs #250. Supersedes #261. Builds on top of #264.


What was done

Based on draft PR #261

The initial implementation was outdated and non‑functional. Main issues:

  • old client-go usage,

  • deprecated numeric types (intint64),

  • incompatible with client-go v1.

This PR completes and modernizes that draft.


Changes in this PR

Updated to client-go v1 (using #264)

  • migrated numeric fields to *int64,
  • updated client calls to new signatures,
  • resolved compile errors after SDK update.

Completed implementation

  • finished controller logic,

  • fixed Create / Update logic

  • implemented reference resolution

Regenerated and aligned code

  • regenerated CRDs, deepcopy, referencers and managed types via make generate,
  • fixed schema inconsistencies after migrating to int64.

Tests & validation

  • repaired tests from the draft,
  • validated against a self‑hosted GitLab CE.

Reconciliation behavior

  • Observe – reads integration state
  • LateInit – fills defaults without overriding user input
  • Create/Update – applies configuration idempotently via API v1
  • UpToDate – compares only fields supported by the API
  • Delete – no‑op (GitLab API supposedly cannot disable this integration?)

Test environment & workflow

Environment:

  • Windows 11 / WSL2 Ubuntu
  • Podman inside WSL2
  • kind cluster (Podman provider):
    KIND_EXPERIMENTAL_PROVIDER=podman kind create cluster --name gitlab
  • Crossplane via Helm:
    helm repo add crossplane-stable https://charts.crossplane.io/stable
    helm install crossplane --namespace crossplane-system --create-namespace crossplane-stable/crossplane
  • Provider built locally:
    make go.build && ./_output/bin/linux_amd64/provider --debug

Test steps:

  1. Create secret with PAT:

    kubectl create secret generic gitlab-credentials -n crossplane-system --from-literal=token=EXAMPLE_VALUE
  2. Apply ProviderConfig:

    apiVersion: gitlab.m.crossplane.io/v1beta1
    kind: ProviderConfig
    metadata:
      name: gitlab-provider
      namespace: default
    spec:
      baseURL: https://REDACTED_URL/
      credentials:
        source: Secret
        method: PersonalAccessToken
        secretRef:
          namespace: crossplane-system
          name: gitlab-credentials
          key: token
  3. Apply examples/projects/integration_mattermost.yaml.

  4. Verify parameters applied correctly.

  5. Update CRD fields and confirm correct reconciliation.

  6. Modify integration manually in GitLab and re‑verify.

  7. Delete resource and confirm no‑op behavior.


Example manifest used during testing

apiVersion: projects.gitlab.m.crossplane.io/v1alpha1
kind: IntegrationMattermost
metadata:
  name: mm-integration-test
spec:
  forProvider:
    projectId: <PROJECT_ID>
    webhook: "https://example.com/hooks/test"
    username: "gitlab-bot"
    notifyOnlyBrokenPipelines: false
    pushEvents: false
    issuesEvents: true
    mergeRequestsEvents: true
    tagPushEvents: false
    noteEvents: true
    pipelineEvents: true
    wikiPageEvents: true

  providerConfigRef:
    kind: ProviderConfig
    name: gitlab-provider

@Vadims-U Vadims-U force-pushed the feature/mm-on-clientgo-v1 branch from 1d25ab6 to cd200fd Compare January 9, 2026 08:36
.gitignore Outdated
Comment on lines +17 to +20
# Local testing
gitlab/
docker-compose.yml
test-resources/
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't consider these files "common" in the sense that everyone uses them for his local dev setup. I case of custom files you want to ignore you can add them to .git/info/exclude which acts as a kind of local gitignore that is not committed.


// Send notifications for broken pipelines.
// +optional
NotifyOnlyBrokenPipelines *bool `json:"notify_only_broken_pipelines,omitempty"`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General rule for API property naming: JSON names should not use underscores and use camel case for the first letter: i.e. projectId instead of projectID or project_id. Does not apply to Go field names: There it is ProjectID instead of ProjectId.

Suggested change
NotifyOnlyBrokenPipelines *bool `json:"notify_only_broken_pipelines,omitempty"`
NotifyOnlyBrokenPipelines *bool `json:"notifyOnlyBrokenPipelines,omitempty"`

Comment on lines +176 to +194
func (e *external) Update(ctx context.Context, mg resource.Managed) (managed.ExternalUpdate, error) {
cr, ok := mg.(*v1alpha1.IntegrationMattermost)
if !ok {
return managed.ExternalUpdate{}, errors.New(errNotIntegrationMattermost)
}

cr.Status.SetConditions(xpv1.Creating())

// Call GitLab Mattermost service API
_, _, err := e.client.SetMattermostService(
*cr.Spec.ForProvider.ProjectID,
projects.GenerateSetMattermostServiceOptions(&cr.Spec.ForProvider),
gitlab.WithContext(ctx))
if err != nil {
return managed.ExternalUpdate{}, errors.Wrap(err, errUpdateFailed)
}

return managed.ExternalUpdate{}, nil
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create and Update functions are basically identical so we can move the call to SetMatterMost() into an extra function and just call it in both.

Comment on lines +133 to +138
if err != nil {
if clients.IsResponseNotFound(res) {
return managed.ExternalObservation{ResourceExists: false}, nil
}
return managed.ExternalObservation{}, errors.Wrap(err, errGetFailed)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be simplified. errors.Wrap() returns nil for nil errors.

Also, false is default value for boolean fields, so ResourceExists: false is implied.

Suggested change
if err != nil {
if clients.IsResponseNotFound(res) {
return managed.ExternalObservation{ResourceExists: false}, nil
}
return managed.ExternalObservation{}, errors.Wrap(err, errGetFailed)
}
if resource.Ignore(clients.IsResponseNotFound, err) != nil {
return managed.ExternalObservation{}, errors.Wrap(err, errGetFailed)
}

@Vadims-U Vadims-U force-pushed the feature/mm-on-clientgo-v1 branch from cd200fd to a221715 Compare January 19, 2026 16:22
Comment on lines +27 to +51
ID int64 `json:"id"`
Title string `json:"title"`
Slug string `json:"slug"`
CreatedAt *metav1.Time `json:"createdAt,omitempty"`
UpdatedAt *metav1.Time `json:"updatedAt,omitempty"`
Active bool `json:"active"`
AlertEvents bool `json:"alertEvents"`
CommitEvents bool `json:"commitEvents"`
ConfidentialIssuesEvents bool `json:"confidentialIssuesEvents"`
ConfidentialNoteEvents bool `json:"confidentialNoteEvents"`
DeploymentEvents bool `json:"deploymentEvents"`
GroupConfidentialMentionEvents bool `json:"groupConfidentialMentionEvents"`
GroupMentionEvents bool `json:"groupMentionEvents"`
IncidentEvents bool `json:"incidentEvents"`
IssuesEvents bool `json:"issuesEvents"`
JobEvents bool `json:"jobEvents"`
MergeRequestsEvents bool `json:"mergeRequestsEvents"`
NoteEvents bool `json:"noteEvents"`
PipelineEvents bool `json:"pipelineEvents"`
PushEvents bool `json:"pushEvents"`
TagPushEvents bool `json:"tagPushEvents"`
VulnerabilityEvents bool `json:"vulnerabilityEvents"`
WikiPageEvents bool `json:"wikiPageEvents"`
CommentOnEventEnabled bool `json:"commentOnEventEnabled"`
Inherited bool `json:"inherited"`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure, but I think they should be optional. Otherwise we might run into an error at the end of the reconcilation when the status update fails due to a missing field.

@Vadims-U Vadims-U force-pushed the feature/mm-on-clientgo-v1 branch from a221715 to f1931d4 Compare January 23, 2026 16:36
@henrysachs
Copy link
Collaborator

@Vadims-U you probably need to merge the latest changes i did to the branch into yours. But I would recommend waiting for my MR to hit master. Also I could imagine that you receive a lot of Merge conflicts because i had to merge all commits into one. Just as a heads up! sorry!

@Vadims-U
Copy link
Contributor Author

@Vadims-U you probably need to merge the latest changes i did to the branch into yours. But I would recommend waiting for my MR to hit master. Also I could imagine that you receive a lot of Merge conflicts because i had to merge all commits into one. Just as a heads up! sorry!

Hi! Nothing to worry about. Thanks for the heads up - I’ll wait for the MR to land in master 👍

@henrysachs
Copy link
Collaborator

@Vadims-U now that my PR has landed would you be so kind and update your PR?

@Vadims-U Vadims-U force-pushed the feature/mm-on-clientgo-v1 branch from f1931d4 to a55949e Compare January 30, 2026 17:49
@henrysachs
Copy link
Collaborator

@Vadims-U I think you forgot to sign your Commit would you be so kind and do this? :)

@Vadims-U Vadims-U force-pushed the feature/mm-on-clientgo-v1 branch 4 times, most recently from 60c4235 to 3a83f8f Compare February 2, 2026 13:06
// PushEventsBranchFilter triggers hook on push events for matching branches only.
// +optional
PushEventsBranchFilter *string `json:"pushEventsBranch_filter,omitempty"`
PushEventsBranchFilter *string `json:"pushEventsBranchFilter,omitempty"`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this change related to this PR? If not it is a breaking change, which we should avoid.

type CommonIntegrationParameters struct {
// Send notifications for broken pipelines.
// +optional
NotifyOnlyBrokenPipelines *bool `json:"notify_only_broken_pipelines,omitempty"`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please replace underscores with camel case.

Comment on lines +87 to +96
@@ -93,7 +93,7 @@ type RunnerProject struct {
// PathWithNamespace is the full path of the project including its namespace.
// This follows the format "namespace/project-path" and is used in URLs.
// +optional
PathWithNamespace string `json:"path_with_namespace"`
PathWithNamespace string `json:"pathWithNamespace"`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a breaking change as well.

Comment on lines +26 to +28
func ptrBool(v bool) *bool { return &v }
func ptrInt64(v int64) *int64 { return &v }
func ptrString(v string) *string { return &v }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use the ptr package instead: https://pkg.go.dev/k8s.io/utils/ptr

Comment on lines +104 to +118
// Int64PtrToInt64Ptr is a no-op now that CRDs use int64
// Kept for API compatibility
func Int64PtrToInt64Ptr(v *int64) *int64 {
return v
}

// IntToInt64 converts int to int64
func IntToInt64(v int) int64 {
return int64(v)
}

// Int64ToInt converts int64 to int
func Int64ToInt(v int64) int {
return int(v)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need this functions?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be removed as soon as Main is rebased into this branch

@henrysachs
Copy link
Collaborator

@Vadims-U would you be so kind and merge master into your branch so everythings up to date?

henrysachs and others added 4 commits February 23, 2026 12:41
Migrate all CRD field types from *int to *int64 to align with GitLab
client-go v1.0 API which uses int64 for all ID fields.

This is NOT a breaking change for users as YAML/JSON does not
distinguish between int and int64 - both serialize as numbers.

Changes:
- Update all API type definitions in apis/namespaced/
- Update all API type definitions in apis/cluster/
- Update helper functions: IsIntEqualToIntPtr -> IsInt64EqualToInt64Ptr
- Update client code to remove unnecessary int() casts
- Update controller code to work with int64 types directly
- Regenerate deepcopy code for all API packages
- Regenerate cluster-scoped files from namespaced templates

Benefits:
- Eliminates hundreds of manual type conversions
- Direct compatibility with GitLab API types
- More maintainable codebase
- Prevents potential overflow issues with large IDs

Files affected:
- All *_types.go files in apis/namespaced/ and apis/cluster/
- Client implementations in pkg/namespaced/clients/
- Controller implementations in pkg/namespaced/controller/
- Generated cluster-scoped equivalents

Related to GitLab client-go v1.0 migration.

Signed-off-by: Henry Sachs <henry.sachs@deutschebahn.com>
Signed-off-by: BoxBoxJason <contact@boxboxjason.dev>
Signed-off-by: Vadims-U <vadims.ubikins@accenture.com>

# Conflicts:
#	apis/cluster/projects/v1alpha1/zz_register.go
#	apis/namespaced/projects/v1alpha1/register.go
#	pkg/cluster/controller/projects/zz_setup.go
#	pkg/namespaced/controller/projects/setup.go
Signed-off-by: Vadims-U <vadims.ubikins@accenture.com>
…lints

Signed-off-by: Vadims-U <vadims.ubikins@accenture.com>
@Vadims-U Vadims-U force-pushed the feature/mm-on-clientgo-v1 branch from 3a83f8f to 307d896 Compare February 25, 2026 15:03
keepN:
format: int64
type: integer
name_regex:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't rename this

keepN:
format: int64
type: integer
name_regex:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't rename this

…lints

Signed-off-by: Vadims-U <vadims.ubikins@accenture.com>
@henrysachs
Copy link
Collaborator

@Vadims-U the DCO is still failing so you need to sign off your commits please

… behavior, full test suite, controller fixes

Signed-off-by: Vadims-U <vadims.ubikins@accenture.com>
@Vadims-U Vadims-U force-pushed the feature/mm-on-clientgo-v1 branch from 2c5241e to 2da3977 Compare March 3, 2026 10:28
Copy link
Collaborator

@MisterMX MisterMX left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thank you very much @Vadims-U!

@MisterMX MisterMX merged commit df8bd77 into crossplane-contrib:master Mar 5, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants