-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
303 additions
and
3 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
# ruff: noqa: F403,F403 | ||
from .handlers_connectors import * | ||
from .handlers_groups import * | ||
from .handlers_resource import * | ||
from .handlers_resource_access import * | ||
from .handlers_services import * |
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 @@ | ||
from datetime import timedelta | ||
|
||
import kopf | ||
|
||
from app.api import TwingateAPIClient | ||
from app.api.exceptions import GraphQLMutationError | ||
from app.crds import TwingateGroupCRD | ||
from app.handlers import fail, success | ||
|
||
|
||
@kopf.on.resume("twingategroup") | ||
@kopf.on.create("twingategroup") | ||
@kopf.on.update("twingategroup") | ||
def twingate_group_create_update(body, spec, logger, memo, patch, **kwargs): | ||
logger.info("twingate_group_reconciler: %s", spec) | ||
settings = memo.twingate_settings | ||
client = TwingateAPIClient(settings) | ||
|
||
if diff := kwargs.get("diff"): | ||
logger.info("Diff: %s", diff) | ||
# If ID changed from `None to value we just created it no need to update | ||
# diff is `(('add', ('spec', 'id'), None, 'R3JvdXA6MjAxNjc4OQ=='),)` | ||
if ( | ||
len(diff) == 1 | ||
and diff[0][0] == "add" | ||
and diff[0][1] == ("spec", "id") | ||
and diff[0][2] is None | ||
): | ||
return success() | ||
|
||
crd = TwingateGroupCRD(**body) | ||
group_id = crd.spec.id | ||
if group_id: | ||
logger.info("Updating group with name='%s'", crd.spec.name) | ||
try: | ||
client.group_update(crd.spec) | ||
except GraphQLMutationError as gqlerr: | ||
logger.error("Failed to update group: %s", gqlerr) | ||
if "does not exist" in gqlerr.message: | ||
patch.spec["id"] = None | ||
return fail(error=gqlerr.message) | ||
else: | ||
logger.info( | ||
"Creating group with name='%s'", | ||
crd.spec.name, | ||
) | ||
group_id = client.group_create(crd.spec) | ||
patch.spec["id"] = group_id | ||
|
||
return success( | ||
twingate_id=group_id, | ||
) | ||
|
||
|
||
@kopf.timer( | ||
"twingategroup", interval=timedelta(hours=10).seconds, initial_delay=60, idle=60 | ||
) | ||
def twingate_group_reconciler(body, spec, logger, memo, patch, **_): | ||
return twingate_group_create_update(body, spec, logger, memo, patch) | ||
|
||
|
||
@kopf.on.delete("twingategroup") | ||
def twingate_group_delete(spec, status, memo, logger, **kwargs): | ||
logger.info("Got a delete request: %s. Status: %s", spec, status) | ||
if not status: | ||
return | ||
|
||
if group_id := spec.get("id"): | ||
logger.info("Deleting group %s", group_id) | ||
client = TwingateAPIClient(memo.twingate_settings) | ||
client.group_delete(group_id) |
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,26 @@ | ||
import pytest | ||
|
||
from app.crds import TwingateGroupCRD | ||
|
||
|
||
@pytest.fixture | ||
def sample_group_object(): | ||
return { | ||
"apiVersion": "twingate.com/v1beta", | ||
"kind": "TwingateConnector", | ||
"metadata": { | ||
"name": "my-group", | ||
"namespace": "default", | ||
"uid": "ad0298c5-b84f-4617-b4a2-d3cbbe9f6a4c", | ||
}, | ||
"spec": { | ||
"name": "My Group", | ||
}, | ||
} | ||
|
||
|
||
def test_group_deserialization(sample_group_object): | ||
group = TwingateGroupCRD(**sample_group_object) | ||
|
||
assert group.metadata.name == "my-group" | ||
assert group.spec.name == "My Group" |
36 changes: 36 additions & 0 deletions
36
deploy/twingate-operator/crds/twingate.com.twingategroup.yaml
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,36 @@ | ||
apiVersion: apiextensions.k8s.io/v1 | ||
kind: CustomResourceDefinition | ||
metadata: | ||
name: twingategroups.twingate.com | ||
spec: | ||
group: twingate.com | ||
versions: | ||
- name: v1beta | ||
served: true | ||
storage: true | ||
schema: | ||
openAPIV3Schema: | ||
type: object | ||
description: "TwingateGroup represents a Group in Twingate." | ||
properties: | ||
spec: | ||
type: object | ||
description: "TwingateGroupSpec defines the desired state of TwingateGroup" | ||
required: ["name"] | ||
properties: | ||
id: | ||
type: string | ||
nullable: true | ||
name: | ||
type: string | ||
description: "Name of the group." | ||
status: | ||
type: object | ||
x-kubernetes-preserve-unknown-fields: true | ||
scope: Namespaced | ||
names: | ||
plural: twingategroups | ||
singular: twingategroup | ||
kind: TwingateGroup | ||
shortNames: | ||
- tgg |
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,6 @@ | ||
apiVersion: twingate.com/v1beta | ||
kind: TwingateGroup | ||
metadata: | ||
name: example | ||
spec: | ||
name: Example Group |
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,33 @@ | ||
import subprocess | ||
|
||
import pytest | ||
|
||
from tests_integration.utils import kubectl_create, kubectl_delete | ||
|
||
|
||
def test_success(unique_resource_name): | ||
result = kubectl_create(f""" | ||
apiVersion: twingate.com/v1beta | ||
kind: TwingateGroup | ||
metadata: | ||
name: {unique_resource_name} | ||
spec: | ||
name: My K8S Group | ||
""") | ||
|
||
assert result.returncode == 0 | ||
kubectl_delete(f"tgg/{unique_resource_name}") | ||
|
||
|
||
def test_name_required(unique_resource_name): | ||
with pytest.raises(subprocess.CalledProcessError) as ex: | ||
kubectl_create(f""" | ||
apiVersion: twingate.com/v1beta | ||
kind: TwingateGroup | ||
metadata: | ||
name: {unique_resource_name} | ||
spec: {{}} | ||
""") | ||
|
||
stderr = ex.value.stderr.decode() | ||
assert "spec.name: Required" in stderr |