From 0dd45cf9671f3c43cddfe030d4b9b288aea9dd25 Mon Sep 17 00:00:00 2001 From: Aditya Singh Date: Fri, 17 Jul 2026 17:18:39 +0530 Subject: [PATCH 1/3] Add Kubernetes integration create/update with ETL definitions Adds CreateIntegrationKubernetes / UpdateIntegrationKubernetes, KubernetesIntegrationInput, and a KubernetesIntegration fragment exposing extractDefinition and transformDefinition. --- .../unreleased/Feature-20260717-100000.yaml | 6 ++ integration.go | 38 +++++++++++ integration_test.go | 68 +++++++++++++++++++ interfaces.go | 1 + testdata/templates/integrations.tpl | 2 +- 5 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 .changes/unreleased/Feature-20260717-100000.yaml diff --git a/.changes/unreleased/Feature-20260717-100000.yaml b/.changes/unreleased/Feature-20260717-100000.yaml new file mode 100644 index 00000000..39c0a565 --- /dev/null +++ b/.changes/unreleased/Feature-20260717-100000.yaml @@ -0,0 +1,6 @@ +kind: Feature +body: Add CreateIntegrationKubernetes and UpdateIntegrationKubernetes client methods + along with KubernetesIntegrationInput and a KubernetesIntegration fragment exposing + ExtractDefinition and TransformDefinition, so a Kubernetes integration's ETL config + can be managed via the API. +time: 2026-07-17T10:00:00.000000Z diff --git a/integration.go b/integration.go index 704763f6..2137ec4e 100644 --- a/integration.go +++ b/integration.go @@ -37,6 +37,11 @@ type GoogleCloudIntegrationFragment struct { TagsOverrideOwnership bool `graphql:"tagsOverrideOwnership"` } +type KubernetesIntegrationFragment struct { + ExtractDefinition string `graphql:"extractDefinition"` + TransformDefinition string `graphql:"transformDefinition"` +} + type NewRelicIntegrationFragment struct { BaseUrl string `graphql:"baseUrl"` AccountKey string `graphql:"accountKey"` @@ -51,7 +56,17 @@ type AWSIntegrationInput struct { RegionOverride *[]string `json:"regionOverride,omitempty"` } +type KubernetesIntegrationInput struct { + ExtractDefinition *Nullable[string] `json:"extractDefinition,omitempty"` + Name *Nullable[string] `json:"name,omitempty"` + TransformDefinition *Nullable[string] `json:"transformDefinition,omitempty"` +} + func (awsIntegrationInput AWSIntegrationInput) GetGraphQLType() string { return "AwsIntegrationInput" } +func (kubernetesIntegrationInput KubernetesIntegrationInput) GetGraphQLType() string { + return "KubernetesIntegrationInput" +} + func (newRelicIntegrationInput NewRelicIntegrationInput) GetGraphQLType() string { return "NewRelicIntegrationInput" } @@ -234,6 +249,29 @@ func (client *Client) UpdateIntegrationGCP(identifier string, input GoogleCloudI return &m.Payload.Integration, HandleErrors(err, m.Payload.Errors) } +func (client *Client) CreateIntegrationKubernetes(input KubernetesIntegrationInput) (*Integration, error) { + var m struct { + Payload IntegrationCreatePayload `graphql:"kubernetesIntegrationCreate(input: $input)"` + } + v := PayloadVariables{ + "input": input, + } + err := client.Mutate(&m, v, WithName("KubernetesIntegrationCreate")) + return &m.Payload.Integration, HandleErrors(err, m.Payload.Errors) +} + +func (client *Client) UpdateIntegrationKubernetes(identifier string, input KubernetesIntegrationInput) (*Integration, error) { + var m struct { + Payload IntegrationUpdatePayload `graphql:"kubernetesIntegrationUpdate(integration: $integration input: $input)"` + } + v := PayloadVariables{ + "integration": *NewIdentifier(identifier), + "input": input, + } + err := client.Mutate(&m, v, WithName("KubernetesIntegrationUpdate")) + return &m.Payload.Integration, HandleErrors(err, m.Payload.Errors) +} + func (client *Client) IntegrationReactivate(identifier string) (*Integration, error) { var m struct { Payload IntegrationReactivatePayload `graphql:"integrationReactivate(integration: $integration)"` diff --git a/integration_test.go b/integration_test.go index 02fa1f77..d0310346 100644 --- a/integration_test.go +++ b/integration_test.go @@ -159,6 +159,40 @@ func TestCreateGoogleCloudIntegration(t *testing.T) { }, result.Projects) } +func TestCreateKubernetesIntegration(t *testing.T) { + // Arrange + testRequest := autopilot.NewTestRequest( + `mutation KubernetesIntegrationCreate($input:KubernetesIntegrationInput!){kubernetesIntegrationCreate(input: $input){integration{{ template "integration_request" }},errors{message,path}}}`, + `{"input": { "name": "Kubernetes", "extractDefinition": "extractors:\n- external_kind: Deployment\n", "transformDefinition": "transforms:\n- infrastructure_resource: Deployment\n" }}`, + `{"data": { + "kubernetesIntegrationCreate": { + "integration": { + {{ template "id1" }}, + "name": "Kubernetes", + "type": "kubernetes", + "createdAt": "2026-07-17T16:25:29.574450Z", + "installedAt": "2026-07-17T16:25:28.541124Z", + "extractDefinition": "extractors:\n- external_kind: Deployment\n", + "transformDefinition": "transforms:\n- infrastructure_resource: Deployment\n" + }, + "errors": [] + }}}`, + ) + client := BestTestClient(t, "integration/create_kubernetes", testRequest) + // Act + result, err := client.CreateIntegrationKubernetes(opslevel.KubernetesIntegrationInput{ + Name: opslevel.RefOf("Kubernetes"), + ExtractDefinition: opslevel.RefOf("extractors:\n- external_kind: Deployment\n"), + TransformDefinition: opslevel.RefOf("transforms:\n- infrastructure_resource: Deployment\n"), + }) + // Assert + autopilot.Equals(t, nil, err) + autopilot.Equals(t, id1, result.Id) + autopilot.Equals(t, "Kubernetes", result.Name) + autopilot.Equals(t, "extractors:\n- external_kind: Deployment\n", result.ExtractDefinition) + autopilot.Equals(t, "transforms:\n- infrastructure_resource: Deployment\n", result.TransformDefinition) +} + func TestCreateNewRelicIntegration(t *testing.T) { // Arrange testRequest := autopilot.NewTestRequest( @@ -368,6 +402,40 @@ func TestUpdateGoogleCloudIntegration(t *testing.T) { }, result.Projects) } +func TestUpdateKubernetesIntegration(t *testing.T) { + // Arrange + testRequest := autopilot.NewTestRequest( + `mutation KubernetesIntegrationUpdate($input:KubernetesIntegrationInput!$integration:IdentifierInput!){kubernetesIntegrationUpdate(integration: $integration input: $input){integration{{ template "integration_request" }},errors{message,path}}}`, + `{"integration": { {{ template "id1" }} }, "input": { "name": "Kubernetes Updated", "extractDefinition": "extractors:\n- external_kind: StatefulSet\n", "transformDefinition": "transforms:\n- infrastructure_resource: StatefulSet\n" }}`, + `{"data": { + "kubernetesIntegrationUpdate": { + "integration": { + {{ template "id1" }}, + "name": "Kubernetes Updated", + "type": "kubernetes", + "createdAt": "2026-07-17T16:25:29.574450Z", + "installedAt": "2026-07-17T16:25:28.541124Z", + "extractDefinition": "extractors:\n- external_kind: StatefulSet\n", + "transformDefinition": "transforms:\n- infrastructure_resource: StatefulSet\n" + }, + "errors": [] + }}}`, + ) + client := BestTestClient(t, "integration/update_kubernetes", testRequest) + // Act + result, err := client.UpdateIntegrationKubernetes(string(id1), opslevel.KubernetesIntegrationInput{ + Name: opslevel.RefOf("Kubernetes Updated"), + ExtractDefinition: opslevel.RefOf("extractors:\n- external_kind: StatefulSet\n"), + TransformDefinition: opslevel.RefOf("transforms:\n- infrastructure_resource: StatefulSet\n"), + }) + // Assert + autopilot.Equals(t, nil, err) + autopilot.Equals(t, id1, result.Id) + autopilot.Equals(t, "Kubernetes Updated", result.Name) + autopilot.Equals(t, "extractors:\n- external_kind: StatefulSet\n", result.ExtractDefinition) + autopilot.Equals(t, "transforms:\n- infrastructure_resource: StatefulSet\n", result.TransformDefinition) +} + func TestUpdateNewRelicIntegration(t *testing.T) { // Arrange testRequest := autopilot.NewTestRequest( diff --git a/interfaces.go b/interfaces.go index 020050c1..24f8bb1b 100644 --- a/interfaces.go +++ b/interfaces.go @@ -68,6 +68,7 @@ type Integration struct { AWSIntegrationFragment `graphql:"... on AwsIntegration"` AzureResourcesIntegrationFragment `graphql:"... on AzureResourcesIntegration"` GoogleCloudIntegrationFragment `graphql:"... on GoogleCloudIntegration"` + KubernetesIntegrationFragment `graphql:"... on KubernetesIntegration"` NewRelicIntegrationFragment `graphql:"... on NewRelicIntegration"` } diff --git a/testdata/templates/integrations.tpl b/testdata/templates/integrations.tpl index 3aadb8a6..e7d65a97 100644 --- a/testdata/templates/integrations.tpl +++ b/testdata/templates/integrations.tpl @@ -40,5 +40,5 @@ {{ end }} {{- define "integration_request" -}} -{id,name,type,displayName,webhookUrl,createdAt,installedAt,... on AwsIntegration{iamRole,externalId,awsTagsOverrideOwnership,ownershipTagKeys,regionOverride},... on AzureResourcesIntegration{aliases,ownershipTagKeys,subscriptionId,tagsOverrideOwnership,tenantId},... on GoogleCloudIntegration{aliases,clientEmail,ownershipTagKeys,projects{id,name,url},tagsOverrideOwnership},... on NewRelicIntegration{baseUrl,accountKey}} +{id,name,type,displayName,webhookUrl,createdAt,installedAt,... on AwsIntegration{iamRole,externalId,awsTagsOverrideOwnership,ownershipTagKeys,regionOverride},... on AzureResourcesIntegration{aliases,ownershipTagKeys,subscriptionId,tagsOverrideOwnership,tenantId},... on GoogleCloudIntegration{aliases,clientEmail,ownershipTagKeys,projects{id,name,url},tagsOverrideOwnership},... on KubernetesIntegration{extractDefinition,transformDefinition},... on NewRelicIntegration{baseUrl,accountKey}} {{- end }} From e7217282bb23e1866b9da466ff210834599921c3 Mon Sep 17 00:00:00 2001 From: Aditya Singh Date: Tue, 21 Jul 2026 22:55:19 +0530 Subject: [PATCH 2/3] Add YAML scalar type and use it for Kubernetes ETL definition inputs --- integration.go | 4 ++-- integration_test.go | 8 ++++---- yaml.go | 6 ++++++ 3 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 yaml.go diff --git a/integration.go b/integration.go index 2137ec4e..5cca3fcc 100644 --- a/integration.go +++ b/integration.go @@ -57,9 +57,9 @@ type AWSIntegrationInput struct { } type KubernetesIntegrationInput struct { - ExtractDefinition *Nullable[string] `json:"extractDefinition,omitempty"` + ExtractDefinition *Nullable[YAML] `json:"extractDefinition,omitempty"` Name *Nullable[string] `json:"name,omitempty"` - TransformDefinition *Nullable[string] `json:"transformDefinition,omitempty"` + TransformDefinition *Nullable[YAML] `json:"transformDefinition,omitempty"` } func (awsIntegrationInput AWSIntegrationInput) GetGraphQLType() string { return "AwsIntegrationInput" } diff --git a/integration_test.go b/integration_test.go index d0310346..fc176cc9 100644 --- a/integration_test.go +++ b/integration_test.go @@ -182,8 +182,8 @@ func TestCreateKubernetesIntegration(t *testing.T) { // Act result, err := client.CreateIntegrationKubernetes(opslevel.KubernetesIntegrationInput{ Name: opslevel.RefOf("Kubernetes"), - ExtractDefinition: opslevel.RefOf("extractors:\n- external_kind: Deployment\n"), - TransformDefinition: opslevel.RefOf("transforms:\n- infrastructure_resource: Deployment\n"), + ExtractDefinition: opslevel.RefOf(opslevel.YAML("extractors:\n- external_kind: Deployment\n")), + TransformDefinition: opslevel.RefOf(opslevel.YAML("transforms:\n- infrastructure_resource: Deployment\n")), }) // Assert autopilot.Equals(t, nil, err) @@ -425,8 +425,8 @@ func TestUpdateKubernetesIntegration(t *testing.T) { // Act result, err := client.UpdateIntegrationKubernetes(string(id1), opslevel.KubernetesIntegrationInput{ Name: opslevel.RefOf("Kubernetes Updated"), - ExtractDefinition: opslevel.RefOf("extractors:\n- external_kind: StatefulSet\n"), - TransformDefinition: opslevel.RefOf("transforms:\n- infrastructure_resource: StatefulSet\n"), + ExtractDefinition: opslevel.RefOf(opslevel.YAML("extractors:\n- external_kind: StatefulSet\n")), + TransformDefinition: opslevel.RefOf(opslevel.YAML("transforms:\n- infrastructure_resource: StatefulSet\n")), }) // Assert autopilot.Equals(t, nil, err) diff --git a/yaml.go b/yaml.go new file mode 100644 index 00000000..42b2ddae --- /dev/null +++ b/yaml.go @@ -0,0 +1,6 @@ +package opslevel + +// YAML represents a YAML document serialized as a string for use with the OpsLevel API. +type YAML string + +func (y YAML) GetGraphQLType() string { return "YAML" } From 45f2007938e8b81e3d67acd4d4a8ca1c55abba86 Mon Sep 17 00:00:00 2001 From: Aditya Singh Date: Tue, 21 Jul 2026 23:45:41 +0530 Subject: [PATCH 3/3] Use *YAML pointers for Kubernetes ETL definition inputs --- integration.go | 4 ++-- integration_test.go | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/integration.go b/integration.go index 5cca3fcc..4a9c8988 100644 --- a/integration.go +++ b/integration.go @@ -57,9 +57,9 @@ type AWSIntegrationInput struct { } type KubernetesIntegrationInput struct { - ExtractDefinition *Nullable[YAML] `json:"extractDefinition,omitempty"` + ExtractDefinition *YAML `json:"extractDefinition,omitempty"` Name *Nullable[string] `json:"name,omitempty"` - TransformDefinition *Nullable[YAML] `json:"transformDefinition,omitempty"` + TransformDefinition *YAML `json:"transformDefinition,omitempty"` } func (awsIntegrationInput AWSIntegrationInput) GetGraphQLType() string { return "AwsIntegrationInput" } diff --git a/integration_test.go b/integration_test.go index fc176cc9..a5d7df75 100644 --- a/integration_test.go +++ b/integration_test.go @@ -179,11 +179,13 @@ func TestCreateKubernetesIntegration(t *testing.T) { }}}`, ) client := BestTestClient(t, "integration/create_kubernetes", testRequest) + extractDefinition := opslevel.YAML("extractors:\n- external_kind: Deployment\n") + transformDefinition := opslevel.YAML("transforms:\n- infrastructure_resource: Deployment\n") // Act result, err := client.CreateIntegrationKubernetes(opslevel.KubernetesIntegrationInput{ Name: opslevel.RefOf("Kubernetes"), - ExtractDefinition: opslevel.RefOf(opslevel.YAML("extractors:\n- external_kind: Deployment\n")), - TransformDefinition: opslevel.RefOf(opslevel.YAML("transforms:\n- infrastructure_resource: Deployment\n")), + ExtractDefinition: &extractDefinition, + TransformDefinition: &transformDefinition, }) // Assert autopilot.Equals(t, nil, err) @@ -422,11 +424,13 @@ func TestUpdateKubernetesIntegration(t *testing.T) { }}}`, ) client := BestTestClient(t, "integration/update_kubernetes", testRequest) + extractDefinition := opslevel.YAML("extractors:\n- external_kind: StatefulSet\n") + transformDefinition := opslevel.YAML("transforms:\n- infrastructure_resource: StatefulSet\n") // Act result, err := client.UpdateIntegrationKubernetes(string(id1), opslevel.KubernetesIntegrationInput{ Name: opslevel.RefOf("Kubernetes Updated"), - ExtractDefinition: opslevel.RefOf(opslevel.YAML("extractors:\n- external_kind: StatefulSet\n")), - TransformDefinition: opslevel.RefOf(opslevel.YAML("transforms:\n- infrastructure_resource: StatefulSet\n")), + ExtractDefinition: &extractDefinition, + TransformDefinition: &transformDefinition, }) // Assert autopilot.Equals(t, nil, err)