diff --git a/.configs/gqlgen.yaml b/.configs/gqlgen.yaml index 20b410861..40b4d1544 100644 --- a/.configs/gqlgen.yaml +++ b/.configs/gqlgen.yaml @@ -37,6 +37,7 @@ autobind: - "github.com/99designs/gqlgen/graphql/introspection" # Without this line in the beginning, a `prelude.resolver.go` is generated - "github.com/nais/api/internal/activitylog" - "github.com/nais/api/internal/alerts" + - "github.com/nais/api/internal/apply" - "github.com/nais/api/internal/auth/authz" - "github.com/nais/api/internal/cost" - "github.com/nais/api/internal/deployment" diff --git a/.mise-tasks/fmt/lua b/.mise-tasks/fmt/lua index 3ff26b4a3..53668ed68 100755 --- a/.mise-tasks/fmt/lua +++ b/.mise-tasks/fmt/lua @@ -3,47 +3,4 @@ set -e -LUA_FORMATTER_VERSION=1.5.6 -BIN_DIR=./bin -LUAFMT="$BIN_DIR/luafmt-$LUA_FORMATTER_VERSION" -LUA_FORMATTER_URL="https://github.com/CppCXY/EmmyLuaCodeStyle/releases/download/$LUA_FORMATTER_VERSION" - -if [ ! -d "$LUAFMT" ]; then - OS=$(uname -s) - ARCH=$(uname -m) - if [ "$OS" = "Darwin" ]; then - if [ "$ARCH" = "x86_64" ]; then - LUA_FORMATTER_FILE=darwin-x64 - else - if [ "$ARCH" = "arm64" ]; then - LUA_FORMATTER_FILE=darwin-arm64 - else - echo "Unsupported architecture: $ARCH on macOS" - exit 1 - fi - fi - elif [ "$OS" = "Linux" ]; then - if [ "$ARCH" = "x86_64" ]; then - LUA_FORMATTER_FILE=linux-x64 - else - if [ "$ARCH" = "aarch64" ]; then - LUA_FORMATTER_FILE=linux-aarch64 - else - echo "Unsupported architecture: $ARCH on Linux" - exit 1 - fi - fi - else - echo "Unsupported OS: $OS" - exit 1 - fi - - mkdir -p "$LUAFMT" - curl -L "$LUA_FORMATTER_URL/$LUA_FORMATTER_FILE.tar.gz" -o /tmp/luafmt.tar.gz - tar -xzf /tmp/luafmt.tar.gz -C "$LUAFMT" - rm /tmp/luafmt.tar.gz - mv "$LUAFMT/$LUA_FORMATTER_FILE/"* "$LUAFMT/" - rmdir "$LUAFMT/$LUA_FORMATTER_FILE" -fi - -$LUAFMT/bin/CodeFormat format -w . --ignores-file ".gitignore" -c ./integration_tests/.editorconfig +CodeFormat format -w . --ignores-file ".gitignore" -c ./integration_tests/.editorconfig diff --git a/AGENTS.md b/AGENTS.md index aaf6a86ca..e97ce7da7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -36,6 +36,7 @@ go test -v -tags=integration_test -run "TestIntegration/" ./integratio 1. **Før du endrer kode**: Les relevante filer for å forstå eksisterende mønstre 2. **Etter endringer i `.graphqls`**: Kjør `mise run generate:graphql` + 1. Hvis det havner modeller i `internal/graph/model/donotuse/models_gen.go`, flytt disse til riktig pakke/domene og generer på nytt 3. **Etter endringer i `.sql`**: Kjør `mise run generate:sql` 4. **Etter alle endringer**: Kjør `mise run test` og `mise run fmt` 5. **Ved kompileringsfeil**: Sjekk at generert kode er oppdatert diff --git a/charts/templates/ingress.yaml b/charts/templates/ingress.yaml index 89a95a301..a71a3721e 100644 --- a/charts/templates/ingress.yaml +++ b/charts/templates/ingress.yaml @@ -36,3 +36,10 @@ spec: name: rest path: /teams/ pathType: Prefix + - backend: + service: + name: "{{ .Release.Name }}" + port: + name: rest + path: /api/v1/ + pathType: Prefix diff --git a/go.mod b/go.mod index d9feb8aad..b118d815a 100644 --- a/go.mod +++ b/go.mod @@ -42,7 +42,7 @@ require ( github.com/nais/bifrost v0.0.0-20260106105449-911627ac2c61 github.com/nais/liberator v0.0.0-20260216142648-ee49a9372bc4 github.com/nais/pgrator/pkg/api v0.0.0-20260219115817-cf954d58c04e - github.com/nais/tester v0.1.0 + github.com/nais/tester v0.1.1 github.com/nais/unleasherator v0.0.0-20251216221129-efebc54203fe github.com/nais/v13s/pkg/api v0.0.0-20260313084546-caca6d4cd742 github.com/patrickmn/go-cache v2.1.0+incompatible diff --git a/go.sum b/go.sum index df2100fb7..62c23ce07 100644 --- a/go.sum +++ b/go.sum @@ -800,6 +800,8 @@ github.com/nais/pgrator/pkg/api v0.0.0-20260219115817-cf954d58c04e h1:FWPwIgFlNj github.com/nais/pgrator/pkg/api v0.0.0-20260219115817-cf954d58c04e/go.mod h1:iDLbi5Ss8Fs6L5+ot8jBLStR5/bdiPgblt7OsN06n50= github.com/nais/tester v0.1.0 h1:dojqSFT3RB8bPZ0Wv0QlBAa/MVPitftpkErC2awVHmU= github.com/nais/tester v0.1.0/go.mod h1:NCQMcgftHz/EXorob1XwDTOqkQmImDqr51YQ2Uea9Pc= +github.com/nais/tester v0.1.1 h1:tpJ5HKpu3mEIWX/mec0Yj0xLHEpt+MwTAsj282n0Py0= +github.com/nais/tester v0.1.1/go.mod h1:NCQMcgftHz/EXorob1XwDTOqkQmImDqr51YQ2Uea9Pc= github.com/nais/unleasherator v0.0.0-20251216221129-efebc54203fe h1:CdRVopOihru4tXVwKZjhg6C8SbPLCQYOhJKpjBZYhjg= github.com/nais/unleasherator v0.0.0-20251216221129-efebc54203fe/go.mod h1:Tiz/1If3WgcfvNhmsO5DiQC+L+1XhBG3KWbIfbjx4EU= github.com/nais/v13s/pkg/api v0.0.0-20260313084546-caca6d4cd742 h1:OQ3n3/UjTf7pO5GzxU+wWRGP3skyOqdoTruS3wm28N4= diff --git a/integration_tests/apply.lua b/integration_tests/apply.lua new file mode 100644 index 000000000..fc4b0fcb2 --- /dev/null +++ b/integration_tests/apply.lua @@ -0,0 +1,455 @@ +local user = User.new("applyer", "apply@example.com", "apply-ext") +local nonMember = User.new("outsider", "outsider@example.com", "outsider-ext") + +local team = Team.new("apply-team", "Apply testing", "#apply-team") +team:addMember(user) + +Test.rest("create application via apply", function(t) + t.addHeader("x-user-email", user:email()) + + t.send("POST", "/api/v1/teams/apply-team/environments/dev/apply", [[ + { + "resources": [ + { + "apiVersion": "nais.io/v1alpha1", + "kind": "Application", + "metadata": { + "name": "my-app", + "namespace": "apply-team" + }, + "spec": { + "image": "example.com/my-app:v1", + "replicas": { + "min": 1, + "max": 2 + } + } + } + ] + } + ]]) + + t.check(200, { + results = { + { + resource = "Application/my-app", + environmentName = "dev", + status = "created", + }, + }, + }) +end) + +Test.k8s("verify resource was created in fake environment", function(t) + t.check("nais.io/v1alpha1", "applications", "dev", "apply-team", "my-app", { + apiVersion = "nais.io/v1alpha1", + kind = "Application", + metadata = { + name = "my-app", + namespace = "apply-team", + }, + spec = { + image = "example.com/my-app:v1", + replicas = { + min = 1, + max = 2, + }, + }, + }) +end) + +Test.rest("update application via apply", function(t) + t.addHeader("x-user-email", user:email()) + + t.send("POST", "/api/v1/teams/apply-team/environments/dev/apply", [[ + { + "resources": [ + { + "apiVersion": "nais.io/v1alpha1", + "kind": "Application", + "metadata": { + "name": "my-app", + "namespace": "apply-team" + }, + "spec": { + "image": "example.com/my-app:v2", + "replicas": { + "min": 2, + "max": 4 + } + } + } + ] + } + ]]) + + t.check(200, { + results = { + { + resource = "Application/my-app", + environmentName = "dev", + status = "applied", + changedFields = { + { + field = "spec.image", + oldValue = "example.com/my-app:v1", + newValue = "example.com/my-app:v2", + }, + { + field = "spec.replicas.max", + oldValue = "2", + newValue = "4", + }, + { + field = "spec.replicas.min", + oldValue = "1", + newValue = "2", + }, + }, + }, + }, + }) +end) + +Test.k8s("verify resource was updated in fake environment", function(t) + t.check("nais.io/v1alpha1", "applications", "dev", "apply-team", "my-app", { + apiVersion = "nais.io/v1alpha1", + kind = "Application", + metadata = { + name = "my-app", + namespace = "apply-team", + }, + spec = { + image = "example.com/my-app:v2", + replicas = { + min = 2, + max = 4, + }, + }, + }) +end) + +Test.rest("disallowed resource kind returns 400", function(t) + t.addHeader("x-user-email", user:email()) + + t.send("POST", "/api/v1/teams/apply-team/environments/dev/apply", [[ + { + "resources": [ + { + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": { + "name": "my-deploy", + "namespace": "apply-team" + }, + "spec": {} + } + ] + } + ]]) + + t.check(400, { + error = Contains("disallowed resource types"), + }) +end) + +Test.rest("non-member gets authorization error", function(t) + t.addHeader("x-user-email", nonMember:email()) + + t.send("POST", "/api/v1/teams/apply-team/environments/dev/apply", [[ + { + "resources": [ + { + "apiVersion": "nais.io/v1alpha1", + "kind": "Application", + "metadata": { + "name": "sneaky-app", + "namespace": "apply-team" + }, + "spec": { + "image": "example.com/sneaky:v1" + } + } + ] + } + ]]) + + t.check(403, { + error = Contains("authorization failed"), + }) +end) + +Test.rest("empty resources array returns 400", function(t) + t.addHeader("x-user-email", user:email()) + + t.send("POST", "/api/v1/teams/apply-team/environments/dev/apply", [[ + { + "resources": [] + } + ]]) + + t.check(400, { + error = Contains("no resources provided"), + }) +end) + +Test.rest("create naisjob via apply", function(t) + t.addHeader("x-user-email", user:email()) + + t.send("POST", "/api/v1/teams/apply-team/environments/dev/apply", [[ + { + "resources": [ + { + "apiVersion": "nais.io/v1", + "kind": "Naisjob", + "metadata": { + "name": "my-job", + "namespace": "apply-team" + }, + "spec": { + "image": "example.com/my-job:v1", + "schedule": "0 * * * *" + } + } + ] + } + ]]) + + t.check(200, { + results = { + { + resource = "Naisjob/my-job", + environmentName = "dev", + status = "created", + }, + }, + }) +end) + +Test.rest("unauthenticated request returns 401", function(t) + t.send("POST", "/api/v1/teams/apply-team/environments/dev/apply", [[ + { + "resources": [ + { + "apiVersion": "nais.io/v1alpha1", + "kind": "Application", + "metadata": { + "name": "unauth-app", + "namespace": "apply-team" + }, + "spec": {} + } + ] + } + ]]) + + t.check(401, { + errors = { + { + message = "Unauthorized", + }, + }, + }) +end) + +Test.gql("activity log contains ApplicationCreatedActivityLogEntry after apply", function(t) + t.addHeader("x-user-email", user:email()) + + t.query(string.format( + [[ + query { + team(slug: "%s") { + activityLog( + first: 20 + filter: { activityTypes: [GENERIC_KUBERNETES_RESOURCE_CREATED] } + ) { + nodes { + __typename + message + actor + resourceType + resourceName + environmentName + ... on ApplicationCreatedActivityLogEntry { + data { + apiVersion + kind + } + } + } + } + } + } + ]], + team:slug() + )) + + -- GENERIC_KUBERNETES_RESOURCE_CREATED is registered for both APP and JOB, so the job entry appears too. + t.check({ + data = { + team = { + activityLog = { + nodes = { + { + __typename = "JobCreatedActivityLogEntry", + message = "Job my-job created", + actor = user:email(), + resourceType = "JOB", + resourceName = "my-job", + environmentName = "dev", + }, + { + __typename = "ApplicationCreatedActivityLogEntry", + message = "Application my-app created", + actor = user:email(), + resourceType = "APP", + resourceName = "my-app", + environmentName = "dev", + data = { + apiVersion = "nais.io/v1alpha1", + kind = "Application", + }, + }, + }, + }, + }, + }, + }) +end) + +Test.gql("activity log contains ApplicationUpdatedActivityLogEntry with changedFields after apply", function(t) + t.addHeader("x-user-email", user:email()) + + t.query(string.format( + [[ + query { + team(slug: "%s") { + activityLog( + first: 20 + filter: { activityTypes: [GENERIC_KUBERNETES_RESOURCE_UPDATED] } + ) { + nodes { + __typename + message + actor + resourceType + resourceName + environmentName + ... on ApplicationUpdatedActivityLogEntry { + data { + apiVersion + kind + changedFields { + field + oldValue + newValue + } + } + } + } + } + } + } + ]], + team:slug() + )) + + t.check({ + data = { + team = { + activityLog = { + nodes = { + { + __typename = "ApplicationUpdatedActivityLogEntry", + message = "Application my-app updated", + actor = user:email(), + resourceType = "APP", + resourceName = "my-app", + environmentName = "dev", + data = { + apiVersion = "nais.io/v1alpha1", + kind = "Application", + changedFields = { + { + field = "spec.image", + oldValue = "example.com/my-app:v1", + newValue = "example.com/my-app:v2", + }, + { + field = "spec.replicas.max", + oldValue = "2", + newValue = "4", + }, + { + field = "spec.replicas.min", + oldValue = "1", + newValue = "2", + }, + }, + }, + }, + }, + }, + }, + }, + }) +end) + +Test.gql("activity log contains JobCreatedActivityLogEntry after apply", function(t) + t.addHeader("x-user-email", user:email()) + + t.query(string.format( + [[ + query { + team(slug: "%s") { + activityLog( + first: 20 + filter: { activityTypes: [GENERIC_KUBERNETES_RESOURCE_CREATED] } + ) { + nodes { + __typename + resourceType + resourceName + environmentName + ... on JobCreatedActivityLogEntry { + data { + apiVersion + kind + } + } + } + } + } + } + ]], + team:slug() + )) + + -- GENERIC_KUBERNETES_RESOURCE_CREATED is registered for both JOB and APP, so application entries appear too. + t.check({ + data = { + team = { + activityLog = { + nodes = { + { + __typename = "JobCreatedActivityLogEntry", + resourceType = "JOB", + resourceName = "my-job", + environmentName = "dev", + data = { + apiVersion = "nais.io/v1", + kind = "Naisjob", + }, + }, + { + __typename = "ApplicationCreatedActivityLogEntry", + resourceType = "APP", + resourceName = "my-app", + environmentName = "dev", + }, + }, + }, + }, + }, + }) +end) diff --git a/integration_tests/teamapi.lua b/integration_tests/teamapi.lua index c06060269..a60d6fe55 100644 --- a/integration_tests/teamapi.lua +++ b/integration_tests/teamapi.lua @@ -14,6 +14,7 @@ otherTeam:addOwner(not_a_member) Test.rest("list team members", function(t) + t.addHeader("Authorization", "Bearer test-pre-shared-key") t.send("GET", string.format("/teams/%s", teamName)) t.check(200, { member = { @@ -24,6 +25,7 @@ Test.rest("list team members", function(t) end) Test.rest("list unknown team", function(t) + t.addHeader("Authorization", "Bearer test-pre-shared-key") t.send("GET", "/teams/no-such-team") t.check(404, { errorMessage = "team not found", @@ -33,6 +35,7 @@ end) Test.rest("invalid team slug", function(t) + t.addHeader("Authorization", "Bearer test-pre-shared-key") t.send("GET", "/teams/invalid-ø-slug") t.check(400, { errorMessage = "A team slug must match the following pattern: \"^[a-z](-?[a-z0-9]+)+$\".", diff --git a/integration_tests/zz_spec.lua b/integration_tests/zz_spec.lua index 08a33402d..b0c5aa66f 100644 --- a/integration_tests/zz_spec.lua +++ b/integration_tests/zz_spec.lua @@ -127,6 +127,13 @@ function TestFunctionTrest.send(method, path, body) print("send") end +--- Add a header to the request +---@param key string +---@param value string +function TestFunctionTrest.addHeader(key, value) + print("addHeader") +end + --- Check the response done by send ---@param status_code number ---@param resp table diff --git a/internal/activitylog/queries.go b/internal/activitylog/queries.go index 7bfc33618..cfbfdd13e 100644 --- a/internal/activitylog/queries.go +++ b/internal/activitylog/queries.go @@ -163,7 +163,10 @@ func toGraphActivityLogEntry(row *activitylogsql.ActivityLogCombinedView) (Activ transformer, ok := knownTransformers[ActivityLogEntryResourceType(row.ResourceType)] if !ok { - return nil, fmt.Errorf("no transformer registered for activity log resource type: %q", row.ResourceType) + if fallbackTransformer == nil { + return nil, fmt.Errorf("no transformer registered for activity log resource type: %q", row.ResourceType) + } + return fallbackTransformer(entry) } return transformer(entry) diff --git a/internal/activitylog/resource.go b/internal/activitylog/resource.go new file mode 100644 index 000000000..d50d785ca --- /dev/null +++ b/internal/activitylog/resource.go @@ -0,0 +1,125 @@ +package activitylog + +import ( + "fmt" + "sync" +) + +var ( + kindResourceTypes = map[string]ActivityLogEntryResourceType{} + kindResourceTypesMu sync.RWMutex +) + +// RegisterKindResourceType registers a mapping from a Kubernetes kind string +// (e.g. "Application") to an ActivityLogEntryResourceType (e.g. "APP"). +// Domain packages call this in their init() so that the apply handler can +// resolve the correct resource type without importing the domain package. +// Panics if the same kind is registered twice. +func RegisterKindResourceType(kind string, resourceType ActivityLogEntryResourceType) { + kindResourceTypesMu.Lock() + defer kindResourceTypesMu.Unlock() + if _, ok := kindResourceTypes[kind]; ok { + panic("kind resource type already registered: " + kind) + } + kindResourceTypes[kind] = resourceType +} + +// ResourceTypeForKind returns the ActivityLogEntryResourceType for the given +// Kubernetes kind, falling back to the kind itself (uppercased) if no mapping +// has been registered. The bool is false only when the kind is empty. +func ResourceTypeForKind(kind string) (ActivityLogEntryResourceType, bool) { + if kind == "" { + return "", false + } + kindResourceTypesMu.RLock() + rt, ok := kindResourceTypes[kind] + kindResourceTypesMu.RUnlock() + if ok { + return rt, true + } + // Unknown kind — use the kind string itself as the resource type so that + // the fallback transformer can handle it. + return ActivityLogEntryResourceType(kind), true +} + +// ResourceChangedField represents a single field that changed during a resource apply operation. +type ResourceChangedField struct { + // Field is the dot-separated path to the changed field, e.g. "spec.replicas". + Field string `json:"field"` + + // OldValue is the string representation of the value before the apply. Nil if the field was added. + OldValue *string `json:"oldValue,omitempty"` + + // NewValue is the string representation of the value after the apply. Nil if the field was removed. + NewValue *string `json:"newValue,omitempty"` +} + +// GenericKubernetesResourceActivityLogEntryData contains the additional data stored with a resource +// created or updated via apply. +type GenericKubernetesResourceActivityLogEntryData struct { + // APIVersion is the apiVersion of the applied resource. + APIVersion string `json:"apiVersion"` + + // Kind is the kind of the applied resource. + Kind string `json:"kind"` + + // ChangedFields lists the fields that changed during the apply. + // Only populated for updates. + ChangedFields []ResourceChangedField `json:"changedFields"` + + // GitHubClaims holds the GitHub Actions OIDC token claims at the time of the + // apply. Only populated when the request was authenticated via a GitHub token. + GitHubClaims *GitHubActorClaims `json:"gitHubClaims,omitempty"` +} + +// GitHubActorClaims holds the GitHub Actions OIDC token claims captured at the +// time of an apply operation. Duplicated from the middleware package to avoid a +// circular import; JSON tags must stay in sync. +type GitHubActorClaims struct { + Ref string `json:"ref"` + Repository string `json:"repository"` + RepositoryID string `json:"repositoryId"` + RunID string `json:"runId"` + RunAttempt string `json:"runAttempt"` + Actor string `json:"actor"` + Workflow string `json:"workflow"` + EventName string `json:"eventName"` + Environment string `json:"environment"` + JobWorkflowRef string `json:"jobWorkflowRef"` +} + +// GenericKubernetesActivityLogEntry is used for resource types that do not have +// a dedicated transformer registered — i.e. kinds that are not modelled in the +// GraphQL API. The uppercase Kind string is used directly as the resource type. +type GenericKubernetesResourceActivityLogEntry struct { + GenericActivityLogEntry + + Data *GenericKubernetesResourceActivityLogEntryData `json:"data"` +} + +var fallbackTransformer Transformer + +// RegisterFallbackTransformer registers a transformer that is called when no +// specific transformer has been registered for a resource type. Only one fallback +// may be registered; a second call panics. +func RegisterFallbackTransformer(t Transformer) { + if fallbackTransformer != nil { + panic("fallback transformer already registered") + } + fallbackTransformer = t +} + +func init() { + RegisterFallbackTransformer(func(entry GenericActivityLogEntry) (ActivityLogEntry, error) { + data, err := UnmarshalData[GenericKubernetesResourceActivityLogEntryData](entry) + if err != nil { + return nil, fmt.Errorf("transforming unsupported resource activity log entry data: %w", err) + } + return GenericKubernetesResourceActivityLogEntry{ + GenericActivityLogEntry: entry.WithMessage( + fmt.Sprintf("%s %s %s", entry.ResourceName, entry.Action, entry.ResourceType), + ), + Data: data, + }, nil + }) +} diff --git a/internal/apply/apply.go b/internal/apply/apply.go new file mode 100644 index 000000000..e6b192246 --- /dev/null +++ b/internal/apply/apply.go @@ -0,0 +1,206 @@ +package apply + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "strings" + + "github.com/nais/api/internal/activitylog" + "github.com/nais/api/internal/auth/authz" + "github.com/nais/api/internal/auth/middleware" + "github.com/nais/api/internal/slug" + "github.com/sirupsen/logrus" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/client-go/dynamic" +) + +const maxBodySize = 5 * 1024 * 1024 // 5 MB + +type Handler struct { + dynamicClientFn DynamicClientFactory + log logrus.FieldLogger +} + +type DynamicClientFactory func(environmentName string, teamSlug slug.Slug) (dynamic.Interface, error) + +func NewHandler(dynamicClientFn DynamicClientFactory, log logrus.FieldLogger) *Handler { + h := &Handler{ + log: log, + dynamicClientFn: dynamicClientFn, + } + + return h +} + +func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + teamSlug := slug.Slug(r.PathValue("teamSlug")) + environmentName := r.PathValue("environment") + + if err := authz.CanApplyKubernetesResource(ctx, teamSlug); err != nil { + writeError(w, http.StatusForbidden, fmt.Sprintf("authorization failed: %s", err)) + return + } + + body, err := io.ReadAll(io.LimitReader(r.Body, maxBodySize+1)) + if err != nil { + writeError(w, http.StatusBadRequest, "unable to read request body") + return + } + if len(body) > maxBodySize { + writeError(w, http.StatusBadRequest, fmt.Sprintf("request body too large (max %d bytes)", maxBodySize)) + return + } + + var req request + if err := json.Unmarshal(body, &req); err != nil { + writeError(w, http.StatusBadRequest, "invalid JSON: "+err.Error()) + return + } + + if len(req.Resources) == 0 { + writeError(w, http.StatusBadRequest, "no resources provided") + return + } + + var disallowed []string + for i, res := range req.Resources { + if !IsAllowed(res) { + disallowed = append(disallowed, fmt.Sprintf("resources[%d]: %s/%s is not an allowed resource type", i, res.GetAPIVersion(), res.GetKind())) + } + } + if len(disallowed) > 0 { + writeError(w, http.StatusBadRequest, fmt.Sprintf("disallowed resource types: %s", strings.Join(disallowed, "; "))) + return + } + + results := make([]ResourceResult, 0, len(req.Resources)) + + client, err := h.dynamicClientFn(environmentName, teamSlug) + if err != nil { + writeError(w, http.StatusInternalServerError, fmt.Sprintf("failed to create client for environment %q: %s", environmentName, err)) + return + } + + for _, res := range req.Resources { + result := h.applyOne(ctx, client, teamSlug, environmentName, &res) + results = append(results, result) + } + + writeJSON(w, http.StatusOK, Response{Results: results}) +} + +func (h *Handler) applyOne( + ctx context.Context, + client dynamic.Interface, + teamSlug slug.Slug, + environmentName string, + res *unstructured.Unstructured, +) ResourceResult { + apiVersion := res.GetAPIVersion() + kind := res.GetKind() + name := res.GetName() + resourceID := kind + "/" + name + + log := h.log.WithFields(logrus.Fields{ + "environment": environmentName, + "team": teamSlug, + "name": name, + "kind": kind, + }) + + if name == "" { + return ResourceResult{ + Resource: resourceID, + EnvironmentName: environmentName, + Status: StatusError, + Error: "resource must have metadata.name", + } + } + + gvr, ok := GVRFor(apiVersion, kind) + if !ok { + return ResourceResult{ + Resource: resourceID, + EnvironmentName: environmentName, + Status: StatusError, + Error: fmt.Sprintf("no GVR mapping for %s/%s", apiVersion, kind), + } + } + + res.SetNamespace(string(teamSlug)) + + applyResult, err := ApplyResource(ctx, client, gvr, res) + if err != nil { + log.WithError(err).Error("applying resource") + return ResourceResult{ + Resource: resourceID, + EnvironmentName: environmentName, + Status: StatusError, + Error: fmt.Sprintf("apply failed: %s", err), + } + } + + var changes []activitylog.ResourceChangedField + status := StatusCreated + if !applyResult.Created { + status = StatusApplied + changes = Diff(applyResult.Before, applyResult.After) + } + + action := activitylog.ActivityLogEntryActionCreated + if !applyResult.Created { + action = activitylog.ActivityLogEntryActionUpdated + } + + resourceType, _ := activitylog.ResourceTypeForKind(kind) + + logData := activitylog.GenericKubernetesResourceActivityLogEntryData{ + APIVersion: apiVersion, + Kind: kind, + ChangedFields: changes, + } + + actor := authz.ActorFromContext(ctx) + if ghActor, ok := actor.User.(*middleware.GitHubRepoActor); ok { + claims := activitylog.GitHubActorClaims(ghActor.Claims) + logData.GitHubClaims = &claims + } + + if err := activitylog.Create(ctx, activitylog.CreateInput{ + Action: action, + Actor: actor.User, + ResourceType: resourceType, + ResourceName: name, + TeamSlug: &teamSlug, + EnvironmentName: &environmentName, + Data: logData, + }); err != nil { + log.WithError(err).Error("creating activity log entry") + } + + return ResourceResult{ + Resource: resourceID, + EnvironmentName: environmentName, + Status: status, + ChangedFields: changes, + } +} + +type request struct { + Resources []unstructured.Unstructured `json:"resources"` +} + +func writeJSON(w http.ResponseWriter, statusCode int, v any) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(statusCode) + _ = json.NewEncoder(w).Encode(v) +} + +func writeError(w http.ResponseWriter, statusCode int, message string) { + writeJSON(w, statusCode, map[string]string{"error": message}) +} diff --git a/internal/apply/client.go b/internal/apply/client.go new file mode 100644 index 000000000..f34ec7e2f --- /dev/null +++ b/internal/apply/client.go @@ -0,0 +1,84 @@ +package apply + +import ( + "context" + "encoding/json" + "fmt" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/dynamic" +) + +const fieldManager = "nais-api" + +// ApplyResult holds the before and after states of a server-side apply operation. +type ApplyResult struct { + // Before is the state of the object before the apply. Nil if the object did not exist. + Before *unstructured.Unstructured + // After is the state of the object after the apply. + After *unstructured.Unstructured + // Created is true if the object was created (did not exist before). + Created bool +} + +// ApplyResource performs a Kubernetes server-side apply for a single resource. +// It fetches the current state (before), applies the resource, and returns both +// before and after states so the caller can diff them. +func ApplyResource( + ctx context.Context, + client dynamic.Interface, + gvr schema.GroupVersionResource, + obj *unstructured.Unstructured, +) (*ApplyResult, error) { + namespace := obj.GetNamespace() + name := obj.GetName() + + if name == "" { + return nil, fmt.Errorf("resource must have a name") + } + if namespace == "" { + return nil, fmt.Errorf("resource must have a namespace") + } + + resourceClient := client.Resource(gvr).Namespace(namespace) + + // Step 1: Get the current state of the object (before-state). + before, err := resourceClient.Get(ctx, name, metav1.GetOptions{}) + if err != nil { + if !apierrors.IsNotFound(err) { + return nil, fmt.Errorf("getting current state of %s/%s: %w", namespace, name, err) + } + before = nil + } + + // Step 2: Marshal the object to JSON for the apply patch. + data, err := json.Marshal(obj.Object) + if err != nil { + return nil, fmt.Errorf("marshaling resource to JSON: %w", err) + } + + // Step 3: Server-side apply using PATCH with ApplyPatchType. + after, err := resourceClient.Patch( + ctx, + name, + types.ApplyPatchType, + data, + metav1.PatchOptions{ + FieldManager: fieldManager, + Force: new(true), + }, + ) + if err != nil { + return nil, fmt.Errorf("applying %s/%s: %w", namespace, name, err) + } + + return &ApplyResult{ + Before: before, + After: after, + Created: before == nil, + }, nil +} diff --git a/internal/apply/diff.go b/internal/apply/diff.go new file mode 100644 index 000000000..364f6d66c --- /dev/null +++ b/internal/apply/diff.go @@ -0,0 +1,245 @@ +package apply + +import ( + "fmt" + "slices" + "strings" + + "github.com/nais/api/internal/activitylog" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +// ignoredTopLevelFields are fields managed by the Kubernetes API server that +// should be excluded from diffs as they are not user-controlled. +var ignoredTopLevelFields = map[string]bool{ + "status": true, +} + +// ignoredMetadataFields are metadata fields managed by the API server. +var ignoredMetadataFields = map[string]bool{ + "resourceVersion": true, + "uid": true, + "generation": true, + "creationTimestamp": true, + "managedFields": true, + "selfLink": true, + "deletionTimestamp": true, + "deletionGracePeriodSeconds": true, +} + +// Diff compares two unstructured Kubernetes objects and returns a list of field changes. +// If before is nil, all fields in after are considered "added". +// If after is nil, all fields in before are considered "removed". +// Server-managed fields (status, metadata.resourceVersion, etc.) are excluded. +func Diff(before, after *unstructured.Unstructured) []activitylog.ResourceChangedField { + var beforeMap, afterMap map[string]any + + if before != nil { + beforeMap = before.Object + } + if after != nil { + afterMap = after.Object + } + + changes := diffMaps(beforeMap, afterMap, "") + + // Sort for deterministic output + slices.SortFunc(changes, func(a, b activitylog.ResourceChangedField) int { + return strings.Compare(a.Field, b.Field) + }) + + return changes +} + +// diffMaps recursively compares two maps and collects field changes. +func diffMaps(before, after map[string]any, prefix string) []activitylog.ResourceChangedField { + var changes []activitylog.ResourceChangedField + + // Collect all keys from both maps + allKeys := map[string]struct{}{} + for k := range before { + allKeys[k] = struct{}{} + } + for k := range after { + allKeys[k] = struct{}{} + } + + for key := range allKeys { + fieldPath := joinPath(prefix, key) + + if shouldIgnoreField(prefix, key) { + continue + } + + oldVal, oldExists := before[key] + newVal, newExists := after[key] + + switch { + case !oldExists && newExists: + // Field was added + changes = append(changes, flattenAdded(fieldPath, newVal)...) + case oldExists && !newExists: + // Field was removed + changes = append(changes, flattenRemoved(fieldPath, oldVal)...) + case oldExists && newExists: + // Field exists in both — compare values + changes = append(changes, diffValues(fieldPath, oldVal, newVal)...) + } + } + + return changes +} + +// diffValues compares two values at a given path and returns changes. +func diffValues(path string, oldVal, newVal any) []activitylog.ResourceChangedField { + // If both are maps, recurse + oldMap, oldIsMap := toMap(oldVal) + newMap, newIsMap := toMap(newVal) + if oldIsMap && newIsMap { + return diffMaps(oldMap, newMap, path) + } + + // If both are slices, compare them + oldSlice, oldIsSlice := toSlice(oldVal) + newSlice, newIsSlice := toSlice(newVal) + if oldIsSlice && newIsSlice { + return diffSlices(path, oldSlice, newSlice) + } + + // Scalar comparison + oldStr := stringify(oldVal) + newStr := stringify(newVal) + if oldStr != newStr { + return []activitylog.ResourceChangedField{{ + Field: path, + OldValue: &oldStr, + NewValue: &newStr, + }} + } + + return nil +} + +// diffSlices compares two slices. If elements are maps, it compares element-by-element. +// Otherwise it compares the slices as a whole. +func diffSlices(path string, oldSlice, newSlice []any) []activitylog.ResourceChangedField { + var changes []activitylog.ResourceChangedField + + maxLen := max(len(oldSlice), len(newSlice)) + for i := range maxLen { + elemPath := fmt.Sprintf("%s[%d]", path, i) + + switch { + case i >= len(oldSlice): + changes = append(changes, flattenAdded(elemPath, newSlice[i])...) + case i >= len(newSlice): + changes = append(changes, flattenRemoved(elemPath, oldSlice[i])...) + default: + changes = append(changes, diffValues(elemPath, oldSlice[i], newSlice[i])...) + } + } + + return changes +} + +// flattenAdded returns FieldChanges for a newly added value (possibly nested). +func flattenAdded(path string, val any) []activitylog.ResourceChangedField { + if m, ok := toMap(val); ok { + var changes []activitylog.ResourceChangedField + for k, v := range m { + changes = append(changes, flattenAdded(joinPath(path, k), v)...) + } + if len(changes) == 0 { + // Empty map added + s := stringify(val) + return []activitylog.ResourceChangedField{{Field: path, NewValue: &s}} + } + return changes + } + + if s, ok := toSlice(val); ok { + var changes []activitylog.ResourceChangedField + for i, v := range s { + changes = append(changes, flattenAdded(fmt.Sprintf("%s[%d]", path, i), v)...) + } + if len(changes) == 0 { + s := stringify(val) + return []activitylog.ResourceChangedField{{Field: path, NewValue: &s}} + } + return changes + } + + s := stringify(val) + return []activitylog.ResourceChangedField{{Field: path, NewValue: &s}} +} + +// flattenRemoved returns FieldChanges for a removed value (possibly nested). +func flattenRemoved(path string, val any) []activitylog.ResourceChangedField { + if m, ok := toMap(val); ok { + var changes []activitylog.ResourceChangedField + for k, v := range m { + changes = append(changes, flattenRemoved(joinPath(path, k), v)...) + } + if len(changes) == 0 { + s := stringify(val) + return []activitylog.ResourceChangedField{{Field: path, OldValue: &s}} + } + return changes + } + + if s, ok := toSlice(val); ok { + var changes []activitylog.ResourceChangedField + for i, v := range s { + changes = append(changes, flattenRemoved(fmt.Sprintf("%s[%d]", path, i), v)...) + } + if len(changes) == 0 { + s := stringify(val) + return []activitylog.ResourceChangedField{{Field: path, OldValue: &s}} + } + return changes + } + + s := stringify(val) + return []activitylog.ResourceChangedField{{Field: path, OldValue: &s}} +} + +// shouldIgnoreField returns true if the field should be excluded from the diff. +func shouldIgnoreField(prefix, key string) bool { + if prefix == "" && ignoredTopLevelFields[key] { + return true + } + + if prefix == "metadata" && ignoredMetadataFields[key] { + return true + } + + return false +} + +// joinPath joins two path segments with a dot separator. +func joinPath(prefix, key string) string { + if prefix == "" { + return key + } + return prefix + "." + key +} + +// toMap attempts to cast a value to map[string]any. +func toMap(val any) (map[string]any, bool) { + m, ok := val.(map[string]any) + return m, ok +} + +// toSlice attempts to cast a value to []any. +func toSlice(val any) ([]any, bool) { + s, ok := val.([]any) + return s, ok +} + +// stringify converts any value to its string representation. +func stringify(v any) string { + if v == nil { + return "" + } + return fmt.Sprintf("%v", v) +} diff --git a/internal/apply/diff_test.go b/internal/apply/diff_test.go new file mode 100644 index 000000000..9fd0ca184 --- /dev/null +++ b/internal/apply/diff_test.go @@ -0,0 +1,844 @@ +package apply + +import ( + "fmt" + "testing" + + "github.com/nais/api/internal/activitylog" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +func TestDiff_BothNil(t *testing.T) { + changes := Diff(nil, nil) + if len(changes) != 0 { + t.Fatalf("expected no changes, got %d", len(changes)) + } +} + +func TestDiff_BeforeNilCreatesAdded(t *testing.T) { + after := &unstructured.Unstructured{ + Object: map[string]any{ + "apiVersion": "nais.io/v1alpha1", + "kind": "Application", + "metadata": map[string]any{ + "name": "my-app", + "namespace": "my-team", + }, + "spec": map[string]any{ + "image": "navikt/my-app:latest", + "replicas": int64(2), + }, + }, + } + + changes := Diff(nil, after) + if len(changes) == 0 { + t.Fatal("expected changes when before is nil") + } + + fieldMap := toFieldMap(changes) + assertFieldExists(t, fieldMap, "apiVersion") + assertFieldExists(t, fieldMap, "kind") + assertFieldExists(t, fieldMap, "metadata.name") + assertFieldExists(t, fieldMap, "metadata.namespace") + assertFieldExists(t, fieldMap, "spec.image") + assertFieldExists(t, fieldMap, "spec.replicas") + + for _, c := range changes { + if c.OldValue != nil { + t.Errorf("expected OldValue to be nil for field %q when before is nil, got %v", c.Field, c.OldValue) + } + } +} + +func TestDiff_AfterNilCreatesRemoved(t *testing.T) { + before := &unstructured.Unstructured{ + Object: map[string]any{ + "apiVersion": "nais.io/v1alpha1", + "kind": "Application", + "spec": map[string]any{ + "image": "navikt/my-app:latest", + }, + }, + } + + changes := Diff(before, nil) + if len(changes) == 0 { + t.Fatal("expected changes when after is nil") + } + + for _, c := range changes { + if c.NewValue != nil { + t.Errorf("expected NewValue to be nil for field %q when after is nil, got %v", c.Field, c.NewValue) + } + } +} + +func TestDiff_IdenticalObjects(t *testing.T) { + obj := map[string]any{ + "apiVersion": "nais.io/v1alpha1", + "kind": "Application", + "metadata": map[string]any{ + "name": "my-app", + "namespace": "my-team", + }, + "spec": map[string]any{ + "image": "navikt/my-app:latest", + "replicas": int64(1), + }, + } + + before := &unstructured.Unstructured{Object: deepCopyMap(obj)} + after := &unstructured.Unstructured{Object: deepCopyMap(obj)} + + changes := Diff(before, after) + if len(changes) != 0 { + t.Fatalf("expected no changes for identical objects, got %d: %+v", len(changes), changes) + } +} + +func TestDiff_ScalarFieldChanged(t *testing.T) { + before := &unstructured.Unstructured{ + Object: map[string]any{ + "apiVersion": "nais.io/v1alpha1", + "kind": "Application", + "spec": map[string]any{ + "image": "navikt/my-app:v1", + "replicas": int64(1), + }, + }, + } + + after := &unstructured.Unstructured{ + Object: map[string]any{ + "apiVersion": "nais.io/v1alpha1", + "kind": "Application", + "spec": map[string]any{ + "image": "navikt/my-app:v2", + "replicas": int64(3), + }, + }, + } + + changes := Diff(before, after) + fieldMap := toFieldMap(changes) + + assertFieldChange(t, fieldMap, "spec.image", "navikt/my-app:v1", "navikt/my-app:v2") + assertFieldChange(t, fieldMap, "spec.replicas", int64(1), int64(3)) + + // apiVersion and kind are unchanged + if _, ok := fieldMap["apiVersion"]; ok { + t.Error("apiVersion should not appear in changes since it did not change") + } + if _, ok := fieldMap["kind"]; ok { + t.Error("kind should not appear in changes since it did not change") + } +} + +func TestDiff_FieldAdded(t *testing.T) { + before := &unstructured.Unstructured{ + Object: map[string]any{ + "spec": map[string]any{ + "image": "navikt/my-app:v1", + }, + }, + } + + after := &unstructured.Unstructured{ + Object: map[string]any{ + "spec": map[string]any{ + "image": "navikt/my-app:v1", + "replicas": int64(2), + }, + }, + } + + changes := Diff(before, after) + fieldMap := toFieldMap(changes) + + if len(changes) != 1 { + t.Fatalf("expected 1 change, got %d: %+v", len(changes), changes) + } + + c := fieldMap["spec.replicas"] + if c.OldValue != nil { + t.Errorf("expected OldValue to be nil, got %v", c.OldValue) + } + if c.NewValue == nil || *c.NewValue != "2" { + t.Errorf("expected NewValue to be %q, got %v", "2", c.NewValue) + } +} + +func TestDiff_FieldRemoved(t *testing.T) { + before := &unstructured.Unstructured{ + Object: map[string]any{ + "spec": map[string]any{ + "image": "navikt/my-app:v1", + "replicas": int64(2), + }, + }, + } + + after := &unstructured.Unstructured{ + Object: map[string]any{ + "spec": map[string]any{ + "image": "navikt/my-app:v1", + }, + }, + } + + changes := Diff(before, after) + fieldMap := toFieldMap(changes) + + if len(changes) != 1 { + t.Fatalf("expected 1 change, got %d: %+v", len(changes), changes) + } + + c := fieldMap["spec.replicas"] + if c.OldValue == nil || *c.OldValue != "2" { + t.Errorf("expected OldValue to be %q, got %v", "2", c.OldValue) + } + if c.NewValue != nil { + t.Errorf("expected NewValue to be nil, got %v", c.NewValue) + } +} + +func TestDiff_NestedMapChanged(t *testing.T) { + before := &unstructured.Unstructured{ + Object: map[string]any{ + "spec": map[string]any{ + "resources": map[string]any{ + "limits": map[string]any{ + "cpu": "500m", + "memory": "128Mi", + }, + }, + }, + }, + } + + after := &unstructured.Unstructured{ + Object: map[string]any{ + "spec": map[string]any{ + "resources": map[string]any{ + "limits": map[string]any{ + "cpu": "1000m", + "memory": "256Mi", + }, + }, + }, + }, + } + + changes := Diff(before, after) + fieldMap := toFieldMap(changes) + + if len(changes) != 2 { + t.Fatalf("expected 2 changes, got %d: %+v", len(changes), changes) + } + + assertFieldChange(t, fieldMap, "spec.resources.limits.cpu", "500m", "1000m") + assertFieldChange(t, fieldMap, "spec.resources.limits.memory", "128Mi", "256Mi") +} + +func TestDiff_NestedMapAdded(t *testing.T) { + before := &unstructured.Unstructured{ + Object: map[string]any{ + "spec": map[string]any{ + "image": "navikt/my-app:v1", + }, + }, + } + + after := &unstructured.Unstructured{ + Object: map[string]any{ + "spec": map[string]any{ + "image": "navikt/my-app:v1", + "resources": map[string]any{ + "limits": map[string]any{ + "cpu": "500m", + }, + }, + }, + }, + } + + changes := Diff(before, after) + fieldMap := toFieldMap(changes) + + if len(changes) != 1 { + t.Fatalf("expected 1 change, got %d: %+v", len(changes), changes) + } + + assertFieldExists(t, fieldMap, "spec.resources.limits.cpu") +} + +func TestDiff_SliceChanged(t *testing.T) { + before := &unstructured.Unstructured{ + Object: map[string]any{ + "spec": map[string]any{ + "ingresses": []any{ + "https://old.example.com", + }, + }, + }, + } + + after := &unstructured.Unstructured{ + Object: map[string]any{ + "spec": map[string]any{ + "ingresses": []any{ + "https://new.example.com", + }, + }, + }, + } + + changes := Diff(before, after) + fieldMap := toFieldMap(changes) + + if len(changes) != 1 { + t.Fatalf("expected 1 change, got %d: %+v", len(changes), changes) + } + + assertFieldChange(t, fieldMap, "spec.ingresses[0]", "https://old.example.com", "https://new.example.com") +} + +func TestDiff_SliceElementAdded(t *testing.T) { + before := &unstructured.Unstructured{ + Object: map[string]any{ + "spec": map[string]any{ + "ingresses": []any{ + "https://one.example.com", + }, + }, + }, + } + + after := &unstructured.Unstructured{ + Object: map[string]any{ + "spec": map[string]any{ + "ingresses": []any{ + "https://one.example.com", + "https://two.example.com", + }, + }, + }, + } + + changes := Diff(before, after) + fieldMap := toFieldMap(changes) + + if len(changes) != 1 { + t.Fatalf("expected 1 change, got %d: %+v", len(changes), changes) + } + + c := fieldMap["spec.ingresses[1]"] + if c.OldValue != nil { + t.Errorf("expected OldValue to be nil, got %v", c.OldValue) + } + if c.NewValue == nil || *c.NewValue != "https://two.example.com" { + t.Errorf("expected NewValue to be %q, got %v", "https://two.example.com", c.NewValue) + } +} + +func TestDiff_SliceElementRemoved(t *testing.T) { + before := &unstructured.Unstructured{ + Object: map[string]any{ + "spec": map[string]any{ + "ingresses": []any{ + "https://one.example.com", + "https://two.example.com", + }, + }, + }, + } + + after := &unstructured.Unstructured{ + Object: map[string]any{ + "spec": map[string]any{ + "ingresses": []any{ + "https://one.example.com", + }, + }, + }, + } + + changes := Diff(before, after) + fieldMap := toFieldMap(changes) + + if len(changes) != 1 { + t.Fatalf("expected 1 change, got %d: %+v", len(changes), changes) + } + + c := fieldMap["spec.ingresses[1]"] + if c.OldValue == nil || *c.OldValue != "https://two.example.com" { + t.Errorf("expected OldValue to be %q, got %v", "https://two.example.com", c.OldValue) + } + if c.NewValue != nil { + t.Errorf("expected NewValue to be nil, got %v", c.NewValue) + } +} + +func TestDiff_SliceOfMapsChanged(t *testing.T) { + before := &unstructured.Unstructured{ + Object: map[string]any{ + "spec": map[string]any{ + "env": []any{ + map[string]any{"name": "FOO", "value": "bar"}, + }, + }, + }, + } + + after := &unstructured.Unstructured{ + Object: map[string]any{ + "spec": map[string]any{ + "env": []any{ + map[string]any{"name": "FOO", "value": "baz"}, + }, + }, + }, + } + + changes := Diff(before, after) + fieldMap := toFieldMap(changes) + + if len(changes) != 1 { + t.Fatalf("expected 1 change, got %d: %+v", len(changes), changes) + } + + assertFieldChange(t, fieldMap, "spec.env[0].value", "bar", "baz") +} + +func TestDiff_IgnoresStatusField(t *testing.T) { + before := &unstructured.Unstructured{ + Object: map[string]any{ + "spec": map[string]any{ + "image": "navikt/my-app:v1", + }, + "status": map[string]any{ + "phase": "Running", + }, + }, + } + + after := &unstructured.Unstructured{ + Object: map[string]any{ + "spec": map[string]any{ + "image": "navikt/my-app:v1", + }, + "status": map[string]any{ + "phase": "Failed", + }, + }, + } + + changes := Diff(before, after) + if len(changes) != 0 { + t.Fatalf("expected no changes (status should be ignored), got %d: %+v", len(changes), changes) + } +} + +func TestDiff_IgnoresServerManagedMetadataFields(t *testing.T) { + before := &unstructured.Unstructured{ + Object: map[string]any{ + "metadata": map[string]any{ + "name": "my-app", + "namespace": "my-team", + "resourceVersion": "12345", + "uid": "abc-def", + "generation": int64(1), + "creationTimestamp": "2024-01-01T00:00:00Z", + "managedFields": []any{map[string]any{"manager": "kubectl"}}, + "selfLink": "/api/v1/namespaces/my-team/my-app", + }, + }, + } + + after := &unstructured.Unstructured{ + Object: map[string]any{ + "metadata": map[string]any{ + "name": "my-app", + "namespace": "my-team", + "resourceVersion": "67890", + "uid": "xyz-123", + "generation": int64(2), + "creationTimestamp": "2024-01-02T00:00:00Z", + "managedFields": []any{map[string]any{"manager": "nais-api"}}, + "selfLink": "/api/v1/namespaces/my-team/my-app-2", + }, + }, + } + + changes := Diff(before, after) + if len(changes) != 0 { + t.Fatalf("expected no changes (server-managed metadata should be ignored), got %d: %+v", len(changes), changes) + } +} + +func TestDiff_PreservesUserMetadataFields(t *testing.T) { + before := &unstructured.Unstructured{ + Object: map[string]any{ + "metadata": map[string]any{ + "name": "my-app", + "namespace": "my-team", + "labels": map[string]any{ + "app": "my-app", + }, + }, + }, + } + + after := &unstructured.Unstructured{ + Object: map[string]any{ + "metadata": map[string]any{ + "name": "my-app", + "namespace": "my-team", + "labels": map[string]any{ + "app": "my-app", + "version": "v2", + }, + }, + }, + } + + changes := Diff(before, after) + fieldMap := toFieldMap(changes) + + if len(changes) != 1 { + t.Fatalf("expected 1 change, got %d: %+v", len(changes), changes) + } + + assertFieldExists(t, fieldMap, "metadata.labels.version") +} + +func TestDiff_AnnotationsChanged(t *testing.T) { + before := &unstructured.Unstructured{ + Object: map[string]any{ + "metadata": map[string]any{ + "name": "my-app", + "annotations": map[string]any{ + "nais.io/cluster": "dev", + }, + }, + }, + } + + after := &unstructured.Unstructured{ + Object: map[string]any{ + "metadata": map[string]any{ + "name": "my-app", + "annotations": map[string]any{ + "nais.io/cluster": "dev", + "nais.io/something": "new", + }, + }, + }, + } + + changes := Diff(before, after) + fieldMap := toFieldMap(changes) + + if len(changes) != 1 { + t.Fatalf("expected 1 change, got %d: %+v", len(changes), changes) + } + + assertFieldExists(t, fieldMap, "metadata.annotations.nais.io/something") +} + +func TestDiff_TypeChangedScalar(t *testing.T) { + before := &unstructured.Unstructured{ + Object: map[string]any{ + "spec": map[string]any{ + "value": "a-string", + }, + }, + } + + after := &unstructured.Unstructured{ + Object: map[string]any{ + "spec": map[string]any{ + "value": int64(42), + }, + }, + } + + changes := Diff(before, after) + fieldMap := toFieldMap(changes) + + if len(changes) != 1 { + t.Fatalf("expected 1 change, got %d: %+v", len(changes), changes) + } + + assertFieldChange(t, fieldMap, "spec.value", "a-string", int64(42)) +} + +func TestDiff_EmptyMapToPopulatedMap(t *testing.T) { + before := &unstructured.Unstructured{ + Object: map[string]any{ + "spec": map[string]any{ + "labels": map[string]any{}, + }, + }, + } + + after := &unstructured.Unstructured{ + Object: map[string]any{ + "spec": map[string]any{ + "labels": map[string]any{ + "app": "my-app", + }, + }, + }, + } + + changes := Diff(before, after) + fieldMap := toFieldMap(changes) + + if len(changes) != 1 { + t.Fatalf("expected 1 change, got %d: %+v", len(changes), changes) + } + + assertFieldExists(t, fieldMap, "spec.labels.app") +} + +func TestDiff_BooleanChange(t *testing.T) { + before := &unstructured.Unstructured{ + Object: map[string]any{ + "spec": map[string]any{ + "skipCaBundle": false, + }, + }, + } + + after := &unstructured.Unstructured{ + Object: map[string]any{ + "spec": map[string]any{ + "skipCaBundle": true, + }, + }, + } + + changes := Diff(before, after) + fieldMap := toFieldMap(changes) + + if len(changes) != 1 { + t.Fatalf("expected 1 change, got %d: %+v", len(changes), changes) + } + + assertFieldChange(t, fieldMap, "spec.skipCaBundle", false, true) +} + +func TestDiff_MultipleTopLevelChanges(t *testing.T) { + before := &unstructured.Unstructured{ + Object: map[string]any{ + "apiVersion": "nais.io/v1alpha1", + "kind": "Application", + "metadata": map[string]any{ + "name": "my-app", + "namespace": "my-team", + }, + "spec": map[string]any{ + "image": "navikt/my-app:v1", + }, + }, + } + + after := &unstructured.Unstructured{ + Object: map[string]any{ + "apiVersion": "nais.io/v1alpha1", + "kind": "Application", + "metadata": map[string]any{ + "name": "my-app", + "namespace": "other-team", + }, + "spec": map[string]any{ + "image": "navikt/my-app:v2", + }, + }, + } + + changes := Diff(before, after) + fieldMap := toFieldMap(changes) + + if len(changes) != 2 { + t.Fatalf("expected 2 changes, got %d: %+v", len(changes), changes) + } + + assertFieldChange(t, fieldMap, "metadata.namespace", "my-team", "other-team") + assertFieldChange(t, fieldMap, "spec.image", "navikt/my-app:v1", "navikt/my-app:v2") +} + +func TestDiff_ResultsAreSorted(t *testing.T) { + before := &unstructured.Unstructured{ + Object: map[string]any{ + "spec": map[string]any{ + "z_field": "a", + "a_field": "a", + "m_field": "a", + }, + }, + } + + after := &unstructured.Unstructured{ + Object: map[string]any{ + "spec": map[string]any{ + "z_field": "b", + "a_field": "b", + "m_field": "b", + }, + }, + } + + changes := Diff(before, after) + if len(changes) != 3 { + t.Fatalf("expected 3 changes, got %d", len(changes)) + } + + if changes[0].Field != "spec.a_field" { + t.Errorf("expected first change to be spec.a_field, got %q", changes[0].Field) + } + if changes[1].Field != "spec.m_field" { + t.Errorf("expected second change to be spec.m_field, got %q", changes[1].Field) + } + if changes[2].Field != "spec.z_field" { + t.Errorf("expected third change to be spec.z_field, got %q", changes[2].Field) + } +} + +func TestDiff_DeletionTimestampIgnored(t *testing.T) { + before := &unstructured.Unstructured{ + Object: map[string]any{ + "metadata": map[string]any{ + "name": "my-app", + }, + }, + } + + after := &unstructured.Unstructured{ + Object: map[string]any{ + "metadata": map[string]any{ + "name": "my-app", + "deletionTimestamp": "2024-01-01T00:00:00Z", + "deletionGracePeriodSeconds": int64(30), + }, + }, + } + + changes := Diff(before, after) + if len(changes) != 0 { + t.Fatalf("expected no changes (deletion fields should be ignored), got %d: %+v", len(changes), changes) + } +} + +func TestDiff_EmptySliceToPopulatedSlice(t *testing.T) { + before := &unstructured.Unstructured{ + Object: map[string]any{ + "spec": map[string]any{ + "ingresses": []any{}, + }, + }, + } + + after := &unstructured.Unstructured{ + Object: map[string]any{ + "spec": map[string]any{ + "ingresses": []any{ + "https://my-app.example.com", + }, + }, + }, + } + + changes := Diff(before, after) + if len(changes) != 1 { + t.Fatalf("expected 1 change, got %d: %+v", len(changes), changes) + } + + if changes[0].Field != "spec.ingresses[0]" { + t.Errorf("expected field spec.ingresses[0], got %q", changes[0].Field) + } +} + +// --- Test helpers --- + +func toFieldMap(changes []activitylog.ResourceChangedField) map[string]activitylog.ResourceChangedField { + m := make(map[string]activitylog.ResourceChangedField, len(changes)) + for _, c := range changes { + m[c.Field] = c + } + return m +} + +func assertFieldExists(t *testing.T, fieldMap map[string]activitylog.ResourceChangedField, field string) { + t.Helper() + if _, ok := fieldMap[field]; !ok { + t.Errorf("expected field %q to be present in changes, but it was not. Fields present: %v", field, fieldMapKeys(fieldMap)) + } +} + +func assertFieldChange(t *testing.T, fieldMap map[string]activitylog.ResourceChangedField, field string, expectedOld, expectedNew any) { + t.Helper() + c, ok := fieldMap[field] + if !ok { + t.Errorf("expected field %q to be present in changes, but it was not. Fields present: %v", field, fieldMapKeys(fieldMap)) + return + } + + wantOld := fmt.Sprintf("%v", expectedOld) + wantNew := fmt.Sprintf("%v", expectedNew) + + if c.OldValue == nil || *c.OldValue != wantOld { + got := "" + if c.OldValue != nil { + got = *c.OldValue + } + t.Errorf("field %q: expected OldValue %q, got %q", field, wantOld, got) + } + if c.NewValue == nil || *c.NewValue != wantNew { + got := "" + if c.NewValue != nil { + got = *c.NewValue + } + t.Errorf("field %q: expected NewValue %q, got %q", field, wantNew, got) + } +} + +func fieldMapKeys(m map[string]activitylog.ResourceChangedField) []string { + keys := make([]string, 0, len(m)) + for k := range m { + keys = append(keys, k) + } + return keys +} + +func deepCopyMap(m map[string]any) map[string]any { + cp := make(map[string]any, len(m)) + for k, v := range m { + switch val := v.(type) { + case map[string]any: + cp[k] = deepCopyMap(val) + case []any: + cp[k] = deepCopySlice(val) + default: + cp[k] = v + } + } + return cp +} + +func deepCopySlice(s []any) []any { + cp := make([]any, len(s)) + for i, v := range s { + switch val := v.(type) { + case map[string]any: + cp[i] = deepCopyMap(val) + case []any: + cp[i] = deepCopySlice(val) + default: + cp[i] = v + } + } + return cp +} diff --git a/internal/apply/model.go b/internal/apply/model.go new file mode 100644 index 000000000..be98704be --- /dev/null +++ b/internal/apply/model.go @@ -0,0 +1,33 @@ +package apply + +import "github.com/nais/api/internal/activitylog" + +// Response is the top-level response returned by the apply endpoint. +type Response struct { + Results []ResourceResult `json:"results"` +} + +// ResourceResult represents the outcome of applying a single resource. +type ResourceResult struct { + // Resource is a human-readable identifier for the resource, e.g. "Application/my-app". + Resource string `json:"resource"` + + // EnvironmentName is the target environment the resource was applied to. + EnvironmentName string `json:"environmentName"` + + // Status is one of "created", "applied", or "error". + Status string `json:"status"` + + // ChangedFields lists the fields that were changed during the apply. + // Only populated when Status is "applied" (i.e. an update, not a create). + ChangedFields []activitylog.ResourceChangedField `json:"changedFields,omitempty"` + + // Error contains the error message if Status is "error". + Error string `json:"error,omitempty"` +} + +const ( + StatusCreated = "created" + StatusApplied = "applied" + StatusError = "error" +) diff --git a/internal/apply/whitelist.go b/internal/apply/whitelist.go new file mode 100644 index 000000000..02044d493 --- /dev/null +++ b/internal/apply/whitelist.go @@ -0,0 +1,175 @@ +package apply + +import ( + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// AllowedResource identifies a Kubernetes resource by its apiVersion and kind. +type AllowedResource struct { + APIVersion string + Kind string +} + +// allowedResources is the single source of truth for which resources can be applied +// through the API. Each entry maps an apiVersion+kind pair to its GroupVersionResource, +// avoiding the need for a discovery client. +var allowedResources = map[AllowedResource]schema.GroupVersionResource{ + // Core workloads + {APIVersion: "nais.io/v1alpha1", Kind: "Application"}: { + Group: "nais.io", Version: "v1alpha1", Resource: "applications", + }, + {APIVersion: "nais.io/v1", Kind: "Naisjob"}: { + Group: "nais.io", Version: "v1", Resource: "naisjobs", + }, + + // Aiven + {APIVersion: "aiven.io/v1alpha1", Kind: "OpenSearch"}: { + Group: "aiven.io", Version: "v1alpha1", Resource: "opensearches", + }, + {APIVersion: "aiven.io/v1alpha1", Kind: "ServiceIntegration"}: { + Group: "aiven.io", Version: "v1alpha1", Resource: "serviceintegrations", + }, + {APIVersion: "aiven.io/v1alpha1", Kind: "Valkey"}: { + Group: "aiven.io", Version: "v1alpha1", Resource: "valkeys", + }, + {APIVersion: "aiven.nais.io/v1", Kind: "AivenApplication"}: { + Group: "aiven.nais.io", Version: "v1", Resource: "aivenapplications", + }, + + // Autoscaling + {APIVersion: "autoscaling/v2", Kind: "HorizontalPodAutoscaler"}: { + Group: "autoscaling", Version: "v2", Resource: "horizontalpodautoscalers", + }, + + // Batch + {APIVersion: "batch/v1", Kind: "Job"}: { + Group: "batch", Version: "v1", Resource: "jobs", + }, + + // BigQuery (Config Connector) + {APIVersion: "bigquery.cnrm.cloud.google.com/v1beta1", Kind: "BigQueryTable"}: { + Group: "bigquery.cnrm.cloud.google.com", Version: "v1beta1", Resource: "bigquerytables", + }, + + // Postgres (NAIS) + {APIVersion: "data.nais.io/v1", Kind: "Postgres"}: { + Group: "data.nais.io", Version: "v1", Resource: "postgres", + }, + + // IAM (Config Connector) + {APIVersion: "iam.cnrm.cloud.google.com/v1beta1", Kind: "IAMPolicyMember"}: { + Group: "iam.cnrm.cloud.google.com", Version: "v1beta1", Resource: "iampolicymembers", + }, + + // Kafka + {APIVersion: "kafka.nais.io/v1", Kind: "Topic"}: { + Group: "kafka.nais.io", Version: "v1", Resource: "topics", + }, + + // Krakend + {APIVersion: "krakend.nais.io/v1", Kind: "ApiEndpoints"}: { + Group: "krakend.nais.io", Version: "v1", Resource: "apiendpoints", + }, + {APIVersion: "krakend.nais.io/v1", Kind: "Krakend"}: { + Group: "krakend.nais.io", Version: "v1", Resource: "krakends", + }, + + // Logging (Config Connector) + {APIVersion: "logging.cnrm.cloud.google.com/v1beta1", Kind: "LoggingLogSink"}: { + Group: "logging.cnrm.cloud.google.com", Version: "v1beta1", Resource: "logginglogsinks", + }, + + // Monitoring (Prometheus Operator) + {APIVersion: "monitoring.coreos.com/v1", Kind: "Probe"}: { + Group: "monitoring.coreos.com", Version: "v1", Resource: "probes", + }, + {APIVersion: "monitoring.coreos.com/v1", Kind: "PrometheusRule"}: { + Group: "monitoring.coreos.com", Version: "v1", Resource: "prometheusrules", + }, + {APIVersion: "monitoring.coreos.com/v1", Kind: "ServiceMonitor"}: { + Group: "monitoring.coreos.com", Version: "v1", Resource: "servicemonitors", + }, + {APIVersion: "monitoring.coreos.com/v1alpha1", Kind: "AlertmanagerConfig"}: { + Group: "monitoring.coreos.com", Version: "v1alpha1", Resource: "alertmanagerconfigs", + }, + + // NAIS + {APIVersion: "nais.io/v1", Kind: "AzureAdApplication"}: { + Group: "nais.io", Version: "v1", Resource: "azureadapplications", + }, + {APIVersion: "nais.io/v1", Kind: "Image"}: { + Group: "nais.io", Version: "v1", Resource: "images", + }, + {APIVersion: "nais.io/v1alpha1", Kind: "Alerts"}: { + Group: "nais.io", Version: "v1alpha1", Resource: "alerts", + }, + {APIVersion: "nais.io/v1alpha1", Kind: "Naisjob"}: { + Group: "nais.io", Version: "v1alpha1", Resource: "naisjobs", + }, + + // Networking + {APIVersion: "networking.k8s.io/v1", Kind: "Ingress"}: { + Group: "networking.k8s.io", Version: "v1", Resource: "ingresses", + }, + {APIVersion: "networking.k8s.io/v1", Kind: "NetworkPolicy"}: { + Group: "networking.k8s.io", Version: "v1", Resource: "networkpolicies", + }, + + // PubSub (Config Connector) + {APIVersion: "pubsub.cnrm.cloud.google.com/v1beta1", Kind: "PubSubTopic"}: { + Group: "pubsub.cnrm.cloud.google.com", Version: "v1beta1", Resource: "pubsubtopics", + }, + + // RBAC + {APIVersion: "rbac.authorization.k8s.io/v1", Kind: "ClusterRole"}: { + Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "clusterroles", + }, + {APIVersion: "rbac.authorization.k8s.io/v1", Kind: "ClusterRoleBinding"}: { + Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "clusterrolebindings", + }, + {APIVersion: "rbac.authorization.k8s.io/v1", Kind: "Role"}: { + Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "roles", + }, + {APIVersion: "rbac.authorization.k8s.io/v1", Kind: "RoleBinding"}: { + Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "rolebindings", + }, + + // Unleash + {APIVersion: "unleash.nais.io/v1", Kind: "ApiToken"}: { + Group: "unleash.nais.io", Version: "v1", Resource: "apitokens", + }, + + // Core v1 + {APIVersion: "v1", Kind: "ConfigMap"}: { + Group: "", Version: "v1", Resource: "configmaps", + }, + {APIVersion: "v1", Kind: "Endpoints"}: { + Group: "", Version: "v1", Resource: "endpoints", + }, + {APIVersion: "v1", Kind: "PersistentVolumeClaim"}: { + Group: "", Version: "v1", Resource: "persistentvolumeclaims", + }, + {APIVersion: "v1", Kind: "Secret"}: { + Group: "", Version: "v1", Resource: "secrets", + }, + {APIVersion: "v1", Kind: "Service"}: { + Group: "", Version: "v1", Resource: "services", + }, + {APIVersion: "v1", Kind: "ServiceAccount"}: { + Group: "", Version: "v1", Resource: "serviceaccounts", + }, +} + +// IsAllowed returns true if the given apiVersion and kind are in the whitelist. +func IsAllowed(res unstructured.Unstructured) bool { + _, ok := allowedResources[AllowedResource{APIVersion: res.GetAPIVersion(), Kind: res.GetKind()}] + return ok +} + +// GVRFor returns the GroupVersionResource for the given apiVersion and kind. +// The second return value is false if the resource is not in the whitelist. +func GVRFor(apiVersion, kind string) (schema.GroupVersionResource, bool) { + gvr, ok := allowedResources[AllowedResource{APIVersion: apiVersion, Kind: kind}] + return gvr, ok +} diff --git a/internal/auth/authz/queries.go b/internal/auth/authz/queries.go index cdfec2cb7..3a24b048e 100644 --- a/internal/auth/authz/queries.go +++ b/internal/auth/authz/queries.go @@ -200,6 +200,10 @@ func CanUpdateJobs(ctx context.Context, teamSlug slug.Slug) error { return requireTeamAuthorization(ctx, teamSlug, "jobs:update") } +func CanApplyKubernetesResource(ctx context.Context, teamSlug slug.Slug) error { + return requireStrictTeamAuthorization(ctx, teamSlug, "k8s_resources:apply") +} + func CanCreateRepositories(ctx context.Context, teamSlug slug.Slug) error { return requireTeamAuthorization(ctx, teamSlug, "repositories:create") } diff --git a/internal/auth/middleware/github_token.go b/internal/auth/middleware/github_token.go index 5667ae84f..17c6b3e95 100644 --- a/internal/auth/middleware/github_token.go +++ b/internal/auth/middleware/github_token.go @@ -16,42 +16,53 @@ import ( "github.com/sirupsen/logrus" ) -// ghClaims represents the claims present in a GitHub OIDC token +// ghClaims represents the claims present in a GitHub OIDC token. // See https://docs.github.com/en/actions/reference/security/oidc#oidc-token-claims. type ghClaims struct { - // Ref string `json:"ref"` - Repository string `json:"repository"` - // RepositoryID string `json:"repository_id"` - // RepositoryOwner string `json:"repository_owner"` - // RepositoryOwnerID string `json:"repository_owner_id"` - // RunID string `json:"run_id"` - // RunNumber string `json:"run_number"` - // RunAttempt string `json:"run_attempt"` - // Actor string `json:"actor"` - // ActorID string `json:"actor_id"` - // Workflow string `json:"workflow"` - // HeadRef string `json:"head_ref"` - // BaseRef string `json:"base_ref"` - // EventName string `json:"event_name"` - // RefType string `json:"ref_type"` - // Environment string `json:"environment"` - // JobWorkflowRef string `json:"job_workflow_ref"` + Ref string `json:"ref"` + Repository string `json:"repository"` + RepositoryID string `json:"repository_id"` + RunID string `json:"run_id"` + RunAttempt string `json:"run_attempt"` + Actor string `json:"actor"` + Workflow string `json:"workflow"` + EventName string `json:"event_name"` + Environment string `json:"environment"` + JobWorkflowRef string `json:"job_workflow_ref"` } -func GitHubOIDC(ctx context.Context, log logrus.FieldLogger) func(next http.Handler) http.Handler { +// GitHubActorClaims holds the subset of GitHub OIDC claims that are stored +// alongside activity log entries for audit purposes. +type GitHubActorClaims struct { + Ref string `json:"ref"` + Repository string `json:"repository"` + RepositoryID string `json:"repositoryID"` + RunID string `json:"runID"` + RunAttempt string `json:"runAttempt"` + Actor string `json:"actor"` + Workflow string `json:"workflow"` + EventName string `json:"eventName"` + Environment string `json:"environment"` + JobWorkflowRef string `json:"jobWorkflowRef"` +} + +const ( + // GitHubOIDCIssuer is the OIDC issuer URL for GitHub Actions tokens. + GitHubOIDCIssuer = "https://token.actions.githubusercontent.com" + + // GitHubOIDCAudience is the expected audience claim in GitHub OIDC tokens. + GitHubOIDCAudience = "api.nais.io" +) + +func GitHubOIDC(ctx context.Context, issuer string, log logrus.FieldLogger) (func(next http.Handler) http.Handler, error) { log = log.WithField("subsystem", "github_oidc") - provider, err := oidc.NewProvider(ctx, "https://token.actions.githubusercontent.com") + provider, err := oidc.NewProvider(ctx, issuer) if err != nil { - log.WithError(err).Error("failed to initialize GitHub OIDC provider. Will not support GitHub OIDC authentication") - return func(sub http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - sub.ServeHTTP(w, r) - }) - } + return nil, fmt.Errorf("initialize GitHub OIDC provider: %w", err) } verifier := provider.Verifier(&oidc.Config{ - ClientID: "api.nais.io", + ClientID: GitHubOIDCAudience, }) return func(next http.Handler) http.Handler { @@ -108,17 +119,30 @@ func GitHubOIDC(ctx context.Context, log logrus.FieldLogger) func(next http.Hand usr := &GitHubRepoActor{ RepositoryName: claims.Repository, TeamSlugs: slices.Collect(maps.Keys(slugs)), + Claims: GitHubActorClaims{ + Ref: claims.Ref, + Repository: claims.Repository, + RepositoryID: claims.RepositoryID, + RunID: claims.RunID, + RunAttempt: claims.RunAttempt, + Actor: claims.Actor, + Workflow: claims.Workflow, + EventName: claims.EventName, + Environment: claims.Environment, + JobWorkflowRef: claims.JobWorkflowRef, + }, } next.ServeHTTP(w, r.WithContext(authz.ContextWithActor(ctx, usr, roles))) } return http.HandlerFunc(fn) - } + }, nil } type GitHubRepoActor struct { RepositoryName string TeamSlugs []slug.Slug + Claims GitHubActorClaims } func (g *GitHubRepoActor) GetID() uuid.UUID { return uuid.Nil } diff --git a/internal/auth/middleware/presharedkey.go b/internal/auth/middleware/presharedkey.go index 85ee88838..bc04446d7 100644 --- a/internal/auth/middleware/presharedkey.go +++ b/internal/auth/middleware/presharedkey.go @@ -35,5 +35,5 @@ func PreSharedKeyAuthentication(preSharedKey string) func(next http.Handler) htt func handleError(w http.ResponseWriter) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusUnauthorized) - _, _ = fmt.Fprintln(w, `{"errors": [{"message": "Unauthorized"}]}`) + _, _ = fmt.Fprintln(w, `{"errors": [{"message": "No valid PSK provided"}]}`) } diff --git a/internal/cmd/api/api.go b/internal/cmd/api/api.go index ddde77ca7..8ed32defb 100644 --- a/internal/cmd/api/api.go +++ b/internal/cmd/api/api.go @@ -14,6 +14,7 @@ import ( aiven_service "github.com/aiven/go-client-codegen" "github.com/joho/godotenv" "github.com/nais/api/internal/activitylog" + "github.com/nais/api/internal/apply" "github.com/nais/api/internal/auth/authn" "github.com/nais/api/internal/auth/middleware" "github.com/nais/api/internal/database" @@ -35,6 +36,7 @@ import ( "github.com/nais/api/internal/persistence/sqlinstance" restserver "github.com/nais/api/internal/rest" "github.com/nais/api/internal/servicemaintenance" + "github.com/nais/api/internal/slug" "github.com/nais/api/internal/thirdparty/aiven" "github.com/nais/api/internal/thirdparty/hookd" fakehookd "github.com/nais/api/internal/thirdparty/hookd/fake" @@ -43,6 +45,7 @@ import ( "github.com/sethvargo/go-envconfig" "github.com/sirupsen/logrus" "golang.org/x/sync/errgroup" + "k8s.io/client-go/dynamic" k8s "k8s.io/client-go/kubernetes" k8sfake "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/rest" @@ -156,13 +159,13 @@ func run(ctx context.Context, cfg *Config, log logrus.FieldLogger) error { } defer watcherMgr.Stop() - mgmtWatcher, err := watcher.NewManager(scheme, kubernetes.ClusterConfigMap{"management": nil}, log.WithField("subsystem", "k8s_watcher"), mgmtWatcherOpts...) + mgmtWatcherMgr, err := watcher.NewManager(scheme, kubernetes.ClusterConfigMap{"management": nil}, log.WithField("subsystem", "k8s_watcher"), mgmtWatcherOpts...) if err != nil { return fmt.Errorf("create k8s watcher manager for management: %w", err) } - defer mgmtWatcher.Stop() + defer mgmtWatcherMgr.Stop() - watchers := watchers.SetupWatchers(ctx, watcherMgr, mgmtWatcher) + watchers := watchers.SetupWatchers(ctx, watcherMgr, mgmtWatcherMgr) pubsubClient, err := pubsub.NewClient(ctx, cfg.GoogleManagementProjectID) if err != nil { @@ -275,6 +278,11 @@ func run(ctx context.Context, cfg *Config, log logrus.FieldLogger) error { } } + githubOIDCMiddleware, err := middleware.GitHubOIDC(ctx, middleware.GitHubOIDCIssuer, log.WithField("subsystem", "github_oidc")) + if err != nil { + return fmt.Errorf("failed to create GitHub OIDC middleware: %w", err) + } + var lokiClientOpts []loki.OptionFunc if addr, ok := os.LookupEnv("LOGGING_LOKI_ADDRESS"); ok { lokiClientOpts = append(lokiClientOpts, loki.WithLocalLoki(addr)) @@ -285,34 +293,46 @@ func run(ctx context.Context, cfg *Config, log logrus.FieldLogger) error { return fmt.Errorf("create loki client: %w", err) } + contextDependencies, err := ConfigureGraph( + ctx, + cfg.Fakes, + watchers, + watcherMgr, + mgmtWatcherMgr, + pool, + clusterConfig, + serviceMaintenanceManager, + aivenClient, + cfg.Aiven.Projects, + vulnMgr, + cfg.Tenant, + cfg.K8s.AllClusterNames(), + hookdClient, + cfg.Unleash.BifrostAPIURL, + cfg.K8s.AllClusterNames(), + cfg.Logging.DefaultLogDestinations(), + notifier, + lokiClient, + cfg.AuditLog.ProjectID, + cfg.AuditLog.Location, + log.WithField("subsystem", "http"), + ) + if err != nil { + return fmt.Errorf("configure graph: %w", err) + } + // HTTP server wg.Go(func() error { return runHTTPServer( ctx, cfg.Fakes, cfg.ListenAddress, - cfg.Tenant, - cfg.K8s.AllClusterNames(), - pool, - clusterConfig, - watchers, - watcherMgr, - mgmtWatcher, + jwtMiddleware, + githubOIDCMiddleware, authHandler, graphHandler, - serviceMaintenanceManager, - aivenClient, - cfg.Aiven.Projects, - vulnMgr, - hookdClient, - cfg.Unleash.BifrostAPIURL, - cfg.K8s.AllClusterNames(), - cfg.Logging.DefaultLogDestinations(), - notifier, - lokiClient, - cfg.AuditLog.ProjectID, - cfg.AuditLog.Location, + contextDependencies, log.WithField("subsystem", "http"), ) }) @@ -321,13 +341,40 @@ func run(ctx context.Context, cfg *Config, log logrus.FieldLogger) error { ctx, cfg.InternalListenAddress, promReg, - []ReadinessChecker{watcherMgr, mgmtWatcher}, + []ReadinessChecker{watcherMgr, mgmtWatcherMgr}, log.WithField("subsystem", "internal_http"), ) }) + var dynamicClientFactory apply.DynamicClientFactory + if cfg.Fakes.WithFakeKubernetes { + dynamicClients := watcherMgr.GetDynamicClients() + dynamicClientFactory = func(environmentName string, _ slug.Slug) (dynamic.Interface, error) { + client, ok := dynamicClients[environmentName] + if !ok { + return nil, fmt.Errorf("unknown environment: %q", environmentName) + } + return client, nil + } + } else { + dynamicClientFactory = clusterConfig.TeamClient + } + wg.Go(func() error { - return restserver.Run(ctx, cfg.RestListenAddress, pool, cfg.RestPreSharedKey, log.WithField("subsystem", "rest")) + return restserver.Run(ctx, restserver.Config{ + ListenAddress: cfg.RestListenAddress, + Pool: pool, + PreSharedKey: cfg.RestPreSharedKey, + DynamicClient: dynamicClientFactory, + ContextMiddleware: contextDependencies, + JWTMiddleware: jwtMiddleware, + GitHubOIDCMiddleware: githubOIDCMiddleware, + AuthHandler: authHandler, + Fakes: restserver.Fakes{ + WithInsecureUserHeader: cfg.Fakes.WithInsecureUserHeader, + }, + Log: log.WithField("subsystem", "rest"), + }) }) wg.Go(func() error { diff --git a/internal/cmd/api/http.go b/internal/cmd/api/http.go index 63c54dde7..71211604e 100644 --- a/internal/cmd/api/http.go +++ b/internal/cmd/api/http.go @@ -76,28 +76,12 @@ func runHTTPServer( ctx context.Context, fakes Fakes, listenAddress string, - tenantName string, - clusters []string, - pool *pgxpool.Pool, - k8sClients apik8s.ClusterConfigMap, - watchers *watchers.Watchers, - watcherMgr *watcher.Manager, - mgmtWatcherMgr *watcher.Manager, + jwtMiddleware func(http.Handler) http.Handler, + githubOIDCMiddleware func(http.Handler) http.Handler, authHandler authn.Handler, graphHandler *handler.Server, - serviceMaintenanceManager *servicemaintenance.Manager, - aivenClient aiven.AivenClient, - aivenProjects aiven.Projects, - vulnMgr *vulnerability.Manager, - hookdClient hookd.Client, - bifrostAPIURL string, - allowedClusters []string, - defaultLogDestinations []logging.SupportedLogDestination, - notifier *notify.Notifier, - lokiClient loki.Client, - auditLogProjectID string, - auditLogLocation string, + contextDependencies func(http.Handler) http.Handler, log logrus.FieldLogger, ) error { router := chi.NewRouter() @@ -105,36 +89,6 @@ func runHTTPServer( otelhttp.NewHandler(playground.Handler("GraphQL playground", "/graphql"), "playground"), ) - graphMiddleware, err := ConfigureGraph( - ctx, - fakes, - watchers, - watcherMgr, - mgmtWatcherMgr, - pool, - k8sClients, - serviceMaintenanceManager, - aivenClient, - aivenProjects, - vulnMgr, - tenantName, - clusters, - hookdClient, - bifrostAPIURL, - allowedClusters, - defaultLogDestinations, - notifier, - lokiClient, - auditLogProjectID, - auditLogLocation, - log, - ) - if err != nil { - return err - } - - contextDependencies := graphMiddleware - router.Route("/graphql", func(r chi.Router) { middlewares := []func(http.Handler) http.Handler{ contextDependencies, @@ -159,7 +113,14 @@ func runHTTPServer( middlewares, middleware.ApiKeyAuthentication(), middleware.Oauth2Authentication(authHandler), - middleware.GitHubOIDC(ctx, log), + ) + + if githubOIDCMiddleware != nil { + middlewares = append(middlewares, githubOIDCMiddleware) + } + + middlewares = append( + middlewares, middleware.RequireAuthenticatedUser(), otelhttp.NewMiddleware( "graphql", diff --git a/internal/cost/costupdater/costupdater_test.go b/internal/cost/costupdater/costupdater_test.go index d4bf52678..d68055278 100644 --- a/internal/cost/costupdater/costupdater_test.go +++ b/internal/cost/costupdater/costupdater_test.go @@ -19,7 +19,7 @@ import ( ) const ( - bigQueryHost = "0.0.0.0:9050" + bigQueryHost = "127.0.0.1:9050" bigQueryUrl = "http://" + bigQueryHost projectID = "nais-io" tenant = "test" diff --git a/internal/database/migrations/0061_add_k8s_resources_apply_authorization.sql b/internal/database/migrations/0061_add_k8s_resources_apply_authorization.sql new file mode 100644 index 000000000..b5f0e70dc --- /dev/null +++ b/internal/database/migrations/0061_add_k8s_resources_apply_authorization.sql @@ -0,0 +1,28 @@ +-- +goose Up +INSERT INTO + authorizations (name, description) +VALUES + ( + 'k8s_resources:apply', + 'Permission to apply generic Kubernetes resources on behalf of a team.' + ) +; + +INSERT INTO + role_authorizations (role_name, authorization_name) +VALUES + ('Team member', 'k8s_resources:apply'), + ('Team owner', 'k8s_resources:apply'), + ('GitHub repository', 'k8s_resources:apply') +; + +-- +goose Down +DELETE FROM role_authorizations +WHERE + authorization_name = 'k8s_resources:apply' +; + +DELETE FROM authorizations +WHERE + name = 'k8s_resources:apply' +; diff --git a/internal/graph/gengql/activitylog.generated.go b/internal/graph/gengql/activitylog.generated.go index 00c289a71..1ea424fc5 100644 --- a/internal/graph/gengql/activitylog.generated.go +++ b/internal/graph/gengql/activitylog.generated.go @@ -532,6 +532,13 @@ func (ec *executionContext) _ActivityLogEntry(ctx context.Context, sel ast.Selec return graphql.Null } return ec._OpenSearchCreatedActivityLogEntry(ctx, sel, obj) + case job.JobUpdatedActivityLogEntry: + return ec._JobUpdatedActivityLogEntry(ctx, sel, &obj) + case *job.JobUpdatedActivityLogEntry: + if obj == nil { + return graphql.Null + } + return ec._JobUpdatedActivityLogEntry(ctx, sel, obj) case job.JobTriggeredActivityLogEntry: return ec._JobTriggeredActivityLogEntry(ctx, sel, &obj) case *job.JobTriggeredActivityLogEntry: @@ -553,6 +560,20 @@ func (ec *executionContext) _ActivityLogEntry(ctx context.Context, sel ast.Selec return graphql.Null } return ec._JobDeletedActivityLogEntry(ctx, sel, obj) + case job.JobCreatedActivityLogEntry: + return ec._JobCreatedActivityLogEntry(ctx, sel, &obj) + case *job.JobCreatedActivityLogEntry: + if obj == nil { + return graphql.Null + } + return ec._JobCreatedActivityLogEntry(ctx, sel, obj) + case activitylog.GenericKubernetesResourceActivityLogEntry: + return ec._GenericKubernetesResourceActivityLogEntry(ctx, sel, &obj) + case *activitylog.GenericKubernetesResourceActivityLogEntry: + if obj == nil { + return graphql.Null + } + return ec._GenericKubernetesResourceActivityLogEntry(ctx, sel, obj) case deploymentactivity.DeploymentActivityLogEntry: return ec._DeploymentActivityLogEntry(ctx, sel, &obj) case *deploymentactivity.DeploymentActivityLogEntry: @@ -595,6 +616,13 @@ func (ec *executionContext) _ActivityLogEntry(ctx context.Context, sel ast.Selec return graphql.Null } return ec._ClusterAuditActivityLogEntry(ctx, sel, obj) + case application.ApplicationUpdatedActivityLogEntry: + return ec._ApplicationUpdatedActivityLogEntry(ctx, sel, &obj) + case *application.ApplicationUpdatedActivityLogEntry: + if obj == nil { + return graphql.Null + } + return ec._ApplicationUpdatedActivityLogEntry(ctx, sel, obj) case application.ApplicationScaledActivityLogEntry: return ec._ApplicationScaledActivityLogEntry(ctx, sel, &obj) case *application.ApplicationScaledActivityLogEntry: @@ -616,6 +644,13 @@ func (ec *executionContext) _ActivityLogEntry(ctx context.Context, sel ast.Selec return graphql.Null } return ec._ApplicationDeletedActivityLogEntry(ctx, sel, obj) + case application.ApplicationCreatedActivityLogEntry: + return ec._ApplicationCreatedActivityLogEntry(ctx, sel, &obj) + case *application.ApplicationCreatedActivityLogEntry: + if obj == nil { + return graphql.Null + } + return ec._ApplicationCreatedActivityLogEntry(ctx, sel, obj) default: if typedObj, ok := obj.(graphql.Marshaler); ok { return typedObj diff --git a/internal/graph/gengql/applications.generated.go b/internal/graph/gengql/applications.generated.go index 11ccc7840..8f9b12fe8 100644 --- a/internal/graph/gengql/applications.generated.go +++ b/internal/graph/gengql/applications.generated.go @@ -1992,6 +1992,277 @@ func (ec *executionContext) fieldContext_ApplicationConnection_edges(_ context.C return fc, nil } +func (ec *executionContext) _ApplicationCreatedActivityLogEntry_id(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationCreatedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_ApplicationCreatedActivityLogEntry_id, + func(ctx context.Context) (any, error) { + return obj.ID(), nil + }, + nil, + ec.marshalNID2githubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋidentᚐIdent, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_ApplicationCreatedActivityLogEntry_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ApplicationCreatedActivityLogEntry", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ApplicationCreatedActivityLogEntry_actor(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationCreatedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_ApplicationCreatedActivityLogEntry_actor, + func(ctx context.Context) (any, error) { + return obj.Actor, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_ApplicationCreatedActivityLogEntry_actor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ApplicationCreatedActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ApplicationCreatedActivityLogEntry_createdAt(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationCreatedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_ApplicationCreatedActivityLogEntry_createdAt, + func(ctx context.Context) (any, error) { + return obj.CreatedAt, nil + }, + nil, + ec.marshalNTime2timeᚐTime, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_ApplicationCreatedActivityLogEntry_createdAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ApplicationCreatedActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Time does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ApplicationCreatedActivityLogEntry_message(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationCreatedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_ApplicationCreatedActivityLogEntry_message, + func(ctx context.Context) (any, error) { + return obj.Message, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_ApplicationCreatedActivityLogEntry_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ApplicationCreatedActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ApplicationCreatedActivityLogEntry_resourceType(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationCreatedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_ApplicationCreatedActivityLogEntry_resourceType, + func(ctx context.Context) (any, error) { + return obj.ResourceType, nil + }, + nil, + ec.marshalNActivityLogEntryResourceType2githubᚗcomᚋnaisᚋapiᚋinternalᚋactivitylogᚐActivityLogEntryResourceType, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_ApplicationCreatedActivityLogEntry_resourceType(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ApplicationCreatedActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ActivityLogEntryResourceType does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ApplicationCreatedActivityLogEntry_resourceName(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationCreatedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_ApplicationCreatedActivityLogEntry_resourceName, + func(ctx context.Context) (any, error) { + return obj.ResourceName, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_ApplicationCreatedActivityLogEntry_resourceName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ApplicationCreatedActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ApplicationCreatedActivityLogEntry_teamSlug(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationCreatedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_ApplicationCreatedActivityLogEntry_teamSlug, + func(ctx context.Context) (any, error) { + return obj.TeamSlug, nil + }, + nil, + ec.marshalNSlug2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋslugᚐSlug, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_ApplicationCreatedActivityLogEntry_teamSlug(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ApplicationCreatedActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Slug does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ApplicationCreatedActivityLogEntry_environmentName(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationCreatedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_ApplicationCreatedActivityLogEntry_environmentName, + func(ctx context.Context) (any, error) { + return obj.EnvironmentName, nil + }, + nil, + ec.marshalOString2ᚖstring, + true, + false, + ) +} + +func (ec *executionContext) fieldContext_ApplicationCreatedActivityLogEntry_environmentName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ApplicationCreatedActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ApplicationCreatedActivityLogEntry_data(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationCreatedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_ApplicationCreatedActivityLogEntry_data, + func(ctx context.Context) (any, error) { + return obj.Data, nil + }, + nil, + ec.marshalNGenericKubernetesResourceActivityLogEntryData2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋactivitylogᚐGenericKubernetesResourceActivityLogEntryData, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_ApplicationCreatedActivityLogEntry_data(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ApplicationCreatedActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "apiVersion": + return ec.fieldContext_GenericKubernetesResourceActivityLogEntryData_apiVersion(ctx, field) + case "kind": + return ec.fieldContext_GenericKubernetesResourceActivityLogEntryData_kind(ctx, field) + case "changedFields": + return ec.fieldContext_GenericKubernetesResourceActivityLogEntryData_changedFields(ctx, field) + case "gitHubClaims": + return ec.fieldContext_GenericKubernetesResourceActivityLogEntryData_gitHubClaims(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type GenericKubernetesResourceActivityLogEntryData", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _ApplicationDeletedActivityLogEntry_id(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationDeletedActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -3301,326 +3572,597 @@ func (ec *executionContext) fieldContext_ApplicationScaledActivityLogEntry_creat return fc, nil } -func (ec *executionContext) _ApplicationScaledActivityLogEntry_message(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationScaledActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _ApplicationScaledActivityLogEntry_message(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationScaledActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_ApplicationScaledActivityLogEntry_message, + func(ctx context.Context) (any, error) { + return obj.Message, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_ApplicationScaledActivityLogEntry_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ApplicationScaledActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ApplicationScaledActivityLogEntry_resourceType(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationScaledActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_ApplicationScaledActivityLogEntry_resourceType, + func(ctx context.Context) (any, error) { + return obj.ResourceType, nil + }, + nil, + ec.marshalNActivityLogEntryResourceType2githubᚗcomᚋnaisᚋapiᚋinternalᚋactivitylogᚐActivityLogEntryResourceType, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_ApplicationScaledActivityLogEntry_resourceType(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ApplicationScaledActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ActivityLogEntryResourceType does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ApplicationScaledActivityLogEntry_resourceName(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationScaledActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_ApplicationScaledActivityLogEntry_resourceName, + func(ctx context.Context) (any, error) { + return obj.ResourceName, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_ApplicationScaledActivityLogEntry_resourceName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ApplicationScaledActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ApplicationScaledActivityLogEntry_teamSlug(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationScaledActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_ApplicationScaledActivityLogEntry_teamSlug, + func(ctx context.Context) (any, error) { + return obj.TeamSlug, nil + }, + nil, + ec.marshalNSlug2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋslugᚐSlug, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_ApplicationScaledActivityLogEntry_teamSlug(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ApplicationScaledActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Slug does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ApplicationScaledActivityLogEntry_environmentName(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationScaledActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_ApplicationScaledActivityLogEntry_environmentName, + func(ctx context.Context) (any, error) { + return obj.EnvironmentName, nil + }, + nil, + ec.marshalOString2ᚖstring, + true, + false, + ) +} + +func (ec *executionContext) fieldContext_ApplicationScaledActivityLogEntry_environmentName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ApplicationScaledActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ApplicationScaledActivityLogEntry_data(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationScaledActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_ApplicationScaledActivityLogEntry_data, + func(ctx context.Context) (any, error) { + return obj.Data, nil + }, + nil, + ec.marshalNApplicationScaledActivityLogEntryData2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋworkloadᚋapplicationᚐApplicationScaledActivityLogEntryData, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_ApplicationScaledActivityLogEntry_data(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ApplicationScaledActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "newSize": + return ec.fieldContext_ApplicationScaledActivityLogEntryData_newSize(ctx, field) + case "direction": + return ec.fieldContext_ApplicationScaledActivityLogEntryData_direction(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ApplicationScaledActivityLogEntryData", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _ApplicationScaledActivityLogEntryData_newSize(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationScaledActivityLogEntryData) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_ApplicationScaledActivityLogEntryData_newSize, + func(ctx context.Context) (any, error) { + return obj.NewSize, nil + }, + nil, + ec.marshalNInt2int, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_ApplicationScaledActivityLogEntryData_newSize(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ApplicationScaledActivityLogEntryData", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ApplicationScaledActivityLogEntryData_direction(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationScaledActivityLogEntryData) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_ApplicationScaledActivityLogEntryData_direction, + func(ctx context.Context) (any, error) { + return obj.Direction, nil + }, + nil, + ec.marshalNScalingDirection2githubᚗcomᚋnaisᚋapiᚋinternalᚋworkloadᚋapplicationᚐScalingDirection, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_ApplicationScaledActivityLogEntryData_direction(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ApplicationScaledActivityLogEntryData", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ScalingDirection does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ApplicationScaling_minInstances(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationScaling) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_ApplicationScaling_minInstances, + func(ctx context.Context) (any, error) { + return obj.MinInstances, nil + }, + nil, + ec.marshalNInt2int, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_ApplicationScaling_minInstances(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ApplicationScaling", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ApplicationScaling_maxInstances(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationScaling) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, - ec.fieldContext_ApplicationScaledActivityLogEntry_message, + ec.fieldContext_ApplicationScaling_maxInstances, func(ctx context.Context) (any, error) { - return obj.Message, nil + return obj.MaxInstances, nil }, nil, - ec.marshalNString2string, + ec.marshalNInt2int, true, true, ) } -func (ec *executionContext) fieldContext_ApplicationScaledActivityLogEntry_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ApplicationScaling_maxInstances(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "ApplicationScaledActivityLogEntry", + Object: "ApplicationScaling", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type Int does not have child fields") }, } return fc, nil } -func (ec *executionContext) _ApplicationScaledActivityLogEntry_resourceType(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationScaledActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _ApplicationScaling_strategies(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationScaling) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, - ec.fieldContext_ApplicationScaledActivityLogEntry_resourceType, + ec.fieldContext_ApplicationScaling_strategies, func(ctx context.Context) (any, error) { - return obj.ResourceType, nil + return obj.Strategies, nil }, nil, - ec.marshalNActivityLogEntryResourceType2githubᚗcomᚋnaisᚋapiᚋinternalᚋactivitylogᚐActivityLogEntryResourceType, + ec.marshalNScalingStrategy2ᚕgithubᚗcomᚋnaisᚋapiᚋinternalᚋworkloadᚋapplicationᚐScalingStrategyᚄ, true, true, ) } -func (ec *executionContext) fieldContext_ApplicationScaledActivityLogEntry_resourceType(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ApplicationScaling_strategies(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "ApplicationScaledActivityLogEntry", + Object: "ApplicationScaling", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type ActivityLogEntryResourceType does not have child fields") + return nil, errors.New("field of type ScalingStrategy does not have child fields") }, } return fc, nil } -func (ec *executionContext) _ApplicationScaledActivityLogEntry_resourceName(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationScaledActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _ApplicationUpdatedActivityLogEntry_id(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationUpdatedActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, - ec.fieldContext_ApplicationScaledActivityLogEntry_resourceName, + ec.fieldContext_ApplicationUpdatedActivityLogEntry_id, func(ctx context.Context) (any, error) { - return obj.ResourceName, nil + return obj.ID(), nil }, nil, - ec.marshalNString2string, + ec.marshalNID2githubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋidentᚐIdent, true, true, ) } -func (ec *executionContext) fieldContext_ApplicationScaledActivityLogEntry_resourceName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ApplicationUpdatedActivityLogEntry_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "ApplicationScaledActivityLogEntry", + Object: "ApplicationUpdatedActivityLogEntry", Field: field, - IsMethod: false, + IsMethod: true, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type ID does not have child fields") }, } return fc, nil } -func (ec *executionContext) _ApplicationScaledActivityLogEntry_teamSlug(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationScaledActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _ApplicationUpdatedActivityLogEntry_actor(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationUpdatedActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, - ec.fieldContext_ApplicationScaledActivityLogEntry_teamSlug, + ec.fieldContext_ApplicationUpdatedActivityLogEntry_actor, func(ctx context.Context) (any, error) { - return obj.TeamSlug, nil + return obj.Actor, nil }, nil, - ec.marshalNSlug2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋslugᚐSlug, + ec.marshalNString2string, true, true, ) } -func (ec *executionContext) fieldContext_ApplicationScaledActivityLogEntry_teamSlug(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ApplicationUpdatedActivityLogEntry_actor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "ApplicationScaledActivityLogEntry", + Object: "ApplicationUpdatedActivityLogEntry", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Slug does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _ApplicationScaledActivityLogEntry_environmentName(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationScaledActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _ApplicationUpdatedActivityLogEntry_createdAt(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationUpdatedActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, - ec.fieldContext_ApplicationScaledActivityLogEntry_environmentName, + ec.fieldContext_ApplicationUpdatedActivityLogEntry_createdAt, func(ctx context.Context) (any, error) { - return obj.EnvironmentName, nil + return obj.CreatedAt, nil }, nil, - ec.marshalOString2ᚖstring, + ec.marshalNTime2timeᚐTime, + true, true, - false, ) } -func (ec *executionContext) fieldContext_ApplicationScaledActivityLogEntry_environmentName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ApplicationUpdatedActivityLogEntry_createdAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "ApplicationScaledActivityLogEntry", + Object: "ApplicationUpdatedActivityLogEntry", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type Time does not have child fields") }, } return fc, nil } -func (ec *executionContext) _ApplicationScaledActivityLogEntry_data(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationScaledActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _ApplicationUpdatedActivityLogEntry_message(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationUpdatedActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, - ec.fieldContext_ApplicationScaledActivityLogEntry_data, + ec.fieldContext_ApplicationUpdatedActivityLogEntry_message, func(ctx context.Context) (any, error) { - return obj.Data, nil + return obj.Message, nil }, nil, - ec.marshalNApplicationScaledActivityLogEntryData2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋworkloadᚋapplicationᚐApplicationScaledActivityLogEntryData, + ec.marshalNString2string, true, true, ) } -func (ec *executionContext) fieldContext_ApplicationScaledActivityLogEntry_data(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ApplicationUpdatedActivityLogEntry_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "ApplicationScaledActivityLogEntry", + Object: "ApplicationUpdatedActivityLogEntry", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "newSize": - return ec.fieldContext_ApplicationScaledActivityLogEntryData_newSize(ctx, field) - case "direction": - return ec.fieldContext_ApplicationScaledActivityLogEntryData_direction(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type ApplicationScaledActivityLogEntryData", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _ApplicationScaledActivityLogEntryData_newSize(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationScaledActivityLogEntryData) (ret graphql.Marshaler) { +func (ec *executionContext) _ApplicationUpdatedActivityLogEntry_resourceType(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationUpdatedActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, - ec.fieldContext_ApplicationScaledActivityLogEntryData_newSize, + ec.fieldContext_ApplicationUpdatedActivityLogEntry_resourceType, func(ctx context.Context) (any, error) { - return obj.NewSize, nil + return obj.ResourceType, nil }, nil, - ec.marshalNInt2int, + ec.marshalNActivityLogEntryResourceType2githubᚗcomᚋnaisᚋapiᚋinternalᚋactivitylogᚐActivityLogEntryResourceType, true, true, ) } -func (ec *executionContext) fieldContext_ApplicationScaledActivityLogEntryData_newSize(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ApplicationUpdatedActivityLogEntry_resourceType(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "ApplicationScaledActivityLogEntryData", + Object: "ApplicationUpdatedActivityLogEntry", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Int does not have child fields") + return nil, errors.New("field of type ActivityLogEntryResourceType does not have child fields") }, } return fc, nil } -func (ec *executionContext) _ApplicationScaledActivityLogEntryData_direction(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationScaledActivityLogEntryData) (ret graphql.Marshaler) { +func (ec *executionContext) _ApplicationUpdatedActivityLogEntry_resourceName(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationUpdatedActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, - ec.fieldContext_ApplicationScaledActivityLogEntryData_direction, + ec.fieldContext_ApplicationUpdatedActivityLogEntry_resourceName, func(ctx context.Context) (any, error) { - return obj.Direction, nil + return obj.ResourceName, nil }, nil, - ec.marshalNScalingDirection2githubᚗcomᚋnaisᚋapiᚋinternalᚋworkloadᚋapplicationᚐScalingDirection, + ec.marshalNString2string, true, true, ) } -func (ec *executionContext) fieldContext_ApplicationScaledActivityLogEntryData_direction(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ApplicationUpdatedActivityLogEntry_resourceName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "ApplicationScaledActivityLogEntryData", + Object: "ApplicationUpdatedActivityLogEntry", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type ScalingDirection does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _ApplicationScaling_minInstances(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationScaling) (ret graphql.Marshaler) { +func (ec *executionContext) _ApplicationUpdatedActivityLogEntry_teamSlug(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationUpdatedActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, - ec.fieldContext_ApplicationScaling_minInstances, + ec.fieldContext_ApplicationUpdatedActivityLogEntry_teamSlug, func(ctx context.Context) (any, error) { - return obj.MinInstances, nil + return obj.TeamSlug, nil }, nil, - ec.marshalNInt2int, + ec.marshalNSlug2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋslugᚐSlug, true, true, ) } -func (ec *executionContext) fieldContext_ApplicationScaling_minInstances(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ApplicationUpdatedActivityLogEntry_teamSlug(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "ApplicationScaling", + Object: "ApplicationUpdatedActivityLogEntry", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Int does not have child fields") + return nil, errors.New("field of type Slug does not have child fields") }, } return fc, nil } -func (ec *executionContext) _ApplicationScaling_maxInstances(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationScaling) (ret graphql.Marshaler) { +func (ec *executionContext) _ApplicationUpdatedActivityLogEntry_environmentName(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationUpdatedActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, - ec.fieldContext_ApplicationScaling_maxInstances, + ec.fieldContext_ApplicationUpdatedActivityLogEntry_environmentName, func(ctx context.Context) (any, error) { - return obj.MaxInstances, nil + return obj.EnvironmentName, nil }, nil, - ec.marshalNInt2int, - true, + ec.marshalOString2ᚖstring, true, + false, ) } -func (ec *executionContext) fieldContext_ApplicationScaling_maxInstances(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ApplicationUpdatedActivityLogEntry_environmentName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "ApplicationScaling", + Object: "ApplicationUpdatedActivityLogEntry", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Int does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _ApplicationScaling_strategies(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationScaling) (ret graphql.Marshaler) { +func (ec *executionContext) _ApplicationUpdatedActivityLogEntry_data(ctx context.Context, field graphql.CollectedField, obj *application.ApplicationUpdatedActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, - ec.fieldContext_ApplicationScaling_strategies, + ec.fieldContext_ApplicationUpdatedActivityLogEntry_data, func(ctx context.Context) (any, error) { - return obj.Strategies, nil + return obj.Data, nil }, nil, - ec.marshalNScalingStrategy2ᚕgithubᚗcomᚋnaisᚋapiᚋinternalᚋworkloadᚋapplicationᚐScalingStrategyᚄ, + ec.marshalNGenericKubernetesResourceActivityLogEntryData2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋactivitylogᚐGenericKubernetesResourceActivityLogEntryData, true, true, ) } -func (ec *executionContext) fieldContext_ApplicationScaling_strategies(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ApplicationUpdatedActivityLogEntry_data(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "ApplicationScaling", + Object: "ApplicationUpdatedActivityLogEntry", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type ScalingStrategy does not have child fields") + switch field.Name { + case "apiVersion": + return ec.fieldContext_GenericKubernetesResourceActivityLogEntryData_apiVersion(ctx, field) + case "kind": + return ec.fieldContext_GenericKubernetesResourceActivityLogEntryData_kind(ctx, field) + case "changedFields": + return ec.fieldContext_GenericKubernetesResourceActivityLogEntryData_changedFields(ctx, field) + case "gitHubClaims": + return ec.fieldContext_GenericKubernetesResourceActivityLogEntryData_gitHubClaims(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type GenericKubernetesResourceActivityLogEntryData", field.Name) }, } return fc, nil @@ -5537,6 +6079,82 @@ func (ec *executionContext) _ApplicationConnection(ctx context.Context, sel ast. return out } +var applicationCreatedActivityLogEntryImplementors = []string{"ApplicationCreatedActivityLogEntry", "ActivityLogEntry", "Node"} + +func (ec *executionContext) _ApplicationCreatedActivityLogEntry(ctx context.Context, sel ast.SelectionSet, obj *application.ApplicationCreatedActivityLogEntry) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, applicationCreatedActivityLogEntryImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ApplicationCreatedActivityLogEntry") + case "id": + out.Values[i] = ec._ApplicationCreatedActivityLogEntry_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "actor": + out.Values[i] = ec._ApplicationCreatedActivityLogEntry_actor(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "createdAt": + out.Values[i] = ec._ApplicationCreatedActivityLogEntry_createdAt(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "message": + out.Values[i] = ec._ApplicationCreatedActivityLogEntry_message(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "resourceType": + out.Values[i] = ec._ApplicationCreatedActivityLogEntry_resourceType(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "resourceName": + out.Values[i] = ec._ApplicationCreatedActivityLogEntry_resourceName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "teamSlug": + out.Values[i] = ec._ApplicationCreatedActivityLogEntry_teamSlug(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "environmentName": + out.Values[i] = ec._ApplicationCreatedActivityLogEntry_environmentName(ctx, field, obj) + case "data": + out.Values[i] = ec._ApplicationCreatedActivityLogEntry_data(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var applicationDeletedActivityLogEntryImplementors = []string{"ApplicationDeletedActivityLogEntry", "ActivityLogEntry", "Node"} func (ec *executionContext) _ApplicationDeletedActivityLogEntry(ctx context.Context, sel ast.SelectionSet, obj *application.ApplicationDeletedActivityLogEntry) graphql.Marshaler { @@ -6217,6 +6835,82 @@ func (ec *executionContext) _ApplicationScaling(ctx context.Context, sel ast.Sel return out } +var applicationUpdatedActivityLogEntryImplementors = []string{"ApplicationUpdatedActivityLogEntry", "ActivityLogEntry", "Node"} + +func (ec *executionContext) _ApplicationUpdatedActivityLogEntry(ctx context.Context, sel ast.SelectionSet, obj *application.ApplicationUpdatedActivityLogEntry) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, applicationUpdatedActivityLogEntryImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ApplicationUpdatedActivityLogEntry") + case "id": + out.Values[i] = ec._ApplicationUpdatedActivityLogEntry_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "actor": + out.Values[i] = ec._ApplicationUpdatedActivityLogEntry_actor(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "createdAt": + out.Values[i] = ec._ApplicationUpdatedActivityLogEntry_createdAt(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "message": + out.Values[i] = ec._ApplicationUpdatedActivityLogEntry_message(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "resourceType": + out.Values[i] = ec._ApplicationUpdatedActivityLogEntry_resourceType(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "resourceName": + out.Values[i] = ec._ApplicationUpdatedActivityLogEntry_resourceName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "teamSlug": + out.Values[i] = ec._ApplicationUpdatedActivityLogEntry_teamSlug(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "environmentName": + out.Values[i] = ec._ApplicationUpdatedActivityLogEntry_environmentName(ctx, field, obj) + case "data": + out.Values[i] = ec._ApplicationUpdatedActivityLogEntry_data(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var cPUScalingStrategyImplementors = []string{"CPUScalingStrategy", "ScalingStrategy"} func (ec *executionContext) _CPUScalingStrategy(ctx context.Context, sel ast.SelectionSet, obj *application.CPUScalingStrategy) graphql.Marshaler { diff --git a/internal/graph/gengql/apply.generated.go b/internal/graph/gengql/apply.generated.go new file mode 100644 index 000000000..f59c4dc99 --- /dev/null +++ b/internal/graph/gengql/apply.generated.go @@ -0,0 +1,1129 @@ +// Code generated by github.com/99designs/gqlgen, DO NOT EDIT. + +package gengql + +import ( + "context" + "errors" + "fmt" + "strconv" + "sync/atomic" + + "github.com/99designs/gqlgen/graphql" + "github.com/nais/api/internal/activitylog" + "github.com/vektah/gqlparser/v2/ast" +) + +// region ************************** generated!.gotpl ************************** + +// endregion ************************** generated!.gotpl ************************** + +// region ***************************** args.gotpl ***************************** + +// endregion ***************************** args.gotpl ***************************** + +// region ************************** directives.gotpl ************************** + +// endregion ************************** directives.gotpl ************************** + +// region **************************** field.gotpl ***************************** + +func (ec *executionContext) _GenericKubernetesResourceActivityLogEntry_id(ctx context.Context, field graphql.CollectedField, obj *activitylog.GenericKubernetesResourceActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_GenericKubernetesResourceActivityLogEntry_id, + func(ctx context.Context) (any, error) { + return obj.ID(), nil + }, + nil, + ec.marshalNID2githubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋidentᚐIdent, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_GenericKubernetesResourceActivityLogEntry_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GenericKubernetesResourceActivityLogEntry", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GenericKubernetesResourceActivityLogEntry_actor(ctx context.Context, field graphql.CollectedField, obj *activitylog.GenericKubernetesResourceActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_GenericKubernetesResourceActivityLogEntry_actor, + func(ctx context.Context) (any, error) { + return obj.Actor, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_GenericKubernetesResourceActivityLogEntry_actor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GenericKubernetesResourceActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GenericKubernetesResourceActivityLogEntry_createdAt(ctx context.Context, field graphql.CollectedField, obj *activitylog.GenericKubernetesResourceActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_GenericKubernetesResourceActivityLogEntry_createdAt, + func(ctx context.Context) (any, error) { + return obj.CreatedAt, nil + }, + nil, + ec.marshalNTime2timeᚐTime, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_GenericKubernetesResourceActivityLogEntry_createdAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GenericKubernetesResourceActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Time does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GenericKubernetesResourceActivityLogEntry_message(ctx context.Context, field graphql.CollectedField, obj *activitylog.GenericKubernetesResourceActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_GenericKubernetesResourceActivityLogEntry_message, + func(ctx context.Context) (any, error) { + return obj.Message, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_GenericKubernetesResourceActivityLogEntry_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GenericKubernetesResourceActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GenericKubernetesResourceActivityLogEntry_resourceType(ctx context.Context, field graphql.CollectedField, obj *activitylog.GenericKubernetesResourceActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_GenericKubernetesResourceActivityLogEntry_resourceType, + func(ctx context.Context) (any, error) { + return obj.ResourceType, nil + }, + nil, + ec.marshalNActivityLogEntryResourceType2githubᚗcomᚋnaisᚋapiᚋinternalᚋactivitylogᚐActivityLogEntryResourceType, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_GenericKubernetesResourceActivityLogEntry_resourceType(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GenericKubernetesResourceActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ActivityLogEntryResourceType does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GenericKubernetesResourceActivityLogEntry_resourceName(ctx context.Context, field graphql.CollectedField, obj *activitylog.GenericKubernetesResourceActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_GenericKubernetesResourceActivityLogEntry_resourceName, + func(ctx context.Context) (any, error) { + return obj.ResourceName, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_GenericKubernetesResourceActivityLogEntry_resourceName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GenericKubernetesResourceActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GenericKubernetesResourceActivityLogEntry_teamSlug(ctx context.Context, field graphql.CollectedField, obj *activitylog.GenericKubernetesResourceActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_GenericKubernetesResourceActivityLogEntry_teamSlug, + func(ctx context.Context) (any, error) { + return obj.TeamSlug, nil + }, + nil, + ec.marshalOSlug2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋslugᚐSlug, + true, + false, + ) +} + +func (ec *executionContext) fieldContext_GenericKubernetesResourceActivityLogEntry_teamSlug(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GenericKubernetesResourceActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Slug does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GenericKubernetesResourceActivityLogEntry_environmentName(ctx context.Context, field graphql.CollectedField, obj *activitylog.GenericKubernetesResourceActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_GenericKubernetesResourceActivityLogEntry_environmentName, + func(ctx context.Context) (any, error) { + return obj.EnvironmentName, nil + }, + nil, + ec.marshalOString2ᚖstring, + true, + false, + ) +} + +func (ec *executionContext) fieldContext_GenericKubernetesResourceActivityLogEntry_environmentName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GenericKubernetesResourceActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GenericKubernetesResourceActivityLogEntry_data(ctx context.Context, field graphql.CollectedField, obj *activitylog.GenericKubernetesResourceActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_GenericKubernetesResourceActivityLogEntry_data, + func(ctx context.Context) (any, error) { + return obj.Data, nil + }, + nil, + ec.marshalNGenericKubernetesResourceActivityLogEntryData2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋactivitylogᚐGenericKubernetesResourceActivityLogEntryData, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_GenericKubernetesResourceActivityLogEntry_data(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GenericKubernetesResourceActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "apiVersion": + return ec.fieldContext_GenericKubernetesResourceActivityLogEntryData_apiVersion(ctx, field) + case "kind": + return ec.fieldContext_GenericKubernetesResourceActivityLogEntryData_kind(ctx, field) + case "changedFields": + return ec.fieldContext_GenericKubernetesResourceActivityLogEntryData_changedFields(ctx, field) + case "gitHubClaims": + return ec.fieldContext_GenericKubernetesResourceActivityLogEntryData_gitHubClaims(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type GenericKubernetesResourceActivityLogEntryData", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _GenericKubernetesResourceActivityLogEntryData_apiVersion(ctx context.Context, field graphql.CollectedField, obj *activitylog.GenericKubernetesResourceActivityLogEntryData) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_GenericKubernetesResourceActivityLogEntryData_apiVersion, + func(ctx context.Context) (any, error) { + return obj.APIVersion, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_GenericKubernetesResourceActivityLogEntryData_apiVersion(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GenericKubernetesResourceActivityLogEntryData", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GenericKubernetesResourceActivityLogEntryData_kind(ctx context.Context, field graphql.CollectedField, obj *activitylog.GenericKubernetesResourceActivityLogEntryData) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_GenericKubernetesResourceActivityLogEntryData_kind, + func(ctx context.Context) (any, error) { + return obj.Kind, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_GenericKubernetesResourceActivityLogEntryData_kind(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GenericKubernetesResourceActivityLogEntryData", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GenericKubernetesResourceActivityLogEntryData_changedFields(ctx context.Context, field graphql.CollectedField, obj *activitylog.GenericKubernetesResourceActivityLogEntryData) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_GenericKubernetesResourceActivityLogEntryData_changedFields, + func(ctx context.Context) (any, error) { + return obj.ChangedFields, nil + }, + nil, + ec.marshalNResourceChangedField2ᚕgithubᚗcomᚋnaisᚋapiᚋinternalᚋactivitylogᚐResourceChangedFieldᚄ, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_GenericKubernetesResourceActivityLogEntryData_changedFields(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GenericKubernetesResourceActivityLogEntryData", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "field": + return ec.fieldContext_ResourceChangedField_field(ctx, field) + case "oldValue": + return ec.fieldContext_ResourceChangedField_oldValue(ctx, field) + case "newValue": + return ec.fieldContext_ResourceChangedField_newValue(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ResourceChangedField", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _GenericKubernetesResourceActivityLogEntryData_gitHubClaims(ctx context.Context, field graphql.CollectedField, obj *activitylog.GenericKubernetesResourceActivityLogEntryData) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_GenericKubernetesResourceActivityLogEntryData_gitHubClaims, + func(ctx context.Context) (any, error) { + return obj.GitHubClaims, nil + }, + nil, + ec.marshalOGitHubActorClaims2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋactivitylogᚐGitHubActorClaims, + true, + false, + ) +} + +func (ec *executionContext) fieldContext_GenericKubernetesResourceActivityLogEntryData_gitHubClaims(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GenericKubernetesResourceActivityLogEntryData", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "ref": + return ec.fieldContext_GitHubActorClaims_ref(ctx, field) + case "repository": + return ec.fieldContext_GitHubActorClaims_repository(ctx, field) + case "repositoryID": + return ec.fieldContext_GitHubActorClaims_repositoryID(ctx, field) + case "runID": + return ec.fieldContext_GitHubActorClaims_runID(ctx, field) + case "runAttempt": + return ec.fieldContext_GitHubActorClaims_runAttempt(ctx, field) + case "actor": + return ec.fieldContext_GitHubActorClaims_actor(ctx, field) + case "workflow": + return ec.fieldContext_GitHubActorClaims_workflow(ctx, field) + case "eventName": + return ec.fieldContext_GitHubActorClaims_eventName(ctx, field) + case "environment": + return ec.fieldContext_GitHubActorClaims_environment(ctx, field) + case "jobWorkflowRef": + return ec.fieldContext_GitHubActorClaims_jobWorkflowRef(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type GitHubActorClaims", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _GitHubActorClaims_ref(ctx context.Context, field graphql.CollectedField, obj *activitylog.GitHubActorClaims) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_GitHubActorClaims_ref, + func(ctx context.Context) (any, error) { + return obj.Ref, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_GitHubActorClaims_ref(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitHubActorClaims", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitHubActorClaims_repository(ctx context.Context, field graphql.CollectedField, obj *activitylog.GitHubActorClaims) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_GitHubActorClaims_repository, + func(ctx context.Context) (any, error) { + return obj.Repository, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_GitHubActorClaims_repository(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitHubActorClaims", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitHubActorClaims_repositoryID(ctx context.Context, field graphql.CollectedField, obj *activitylog.GitHubActorClaims) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_GitHubActorClaims_repositoryID, + func(ctx context.Context) (any, error) { + return obj.RepositoryID, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_GitHubActorClaims_repositoryID(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitHubActorClaims", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitHubActorClaims_runID(ctx context.Context, field graphql.CollectedField, obj *activitylog.GitHubActorClaims) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_GitHubActorClaims_runID, + func(ctx context.Context) (any, error) { + return obj.RunID, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_GitHubActorClaims_runID(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitHubActorClaims", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitHubActorClaims_runAttempt(ctx context.Context, field graphql.CollectedField, obj *activitylog.GitHubActorClaims) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_GitHubActorClaims_runAttempt, + func(ctx context.Context) (any, error) { + return obj.RunAttempt, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_GitHubActorClaims_runAttempt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitHubActorClaims", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitHubActorClaims_actor(ctx context.Context, field graphql.CollectedField, obj *activitylog.GitHubActorClaims) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_GitHubActorClaims_actor, + func(ctx context.Context) (any, error) { + return obj.Actor, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_GitHubActorClaims_actor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitHubActorClaims", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitHubActorClaims_workflow(ctx context.Context, field graphql.CollectedField, obj *activitylog.GitHubActorClaims) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_GitHubActorClaims_workflow, + func(ctx context.Context) (any, error) { + return obj.Workflow, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_GitHubActorClaims_workflow(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitHubActorClaims", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitHubActorClaims_eventName(ctx context.Context, field graphql.CollectedField, obj *activitylog.GitHubActorClaims) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_GitHubActorClaims_eventName, + func(ctx context.Context) (any, error) { + return obj.EventName, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_GitHubActorClaims_eventName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitHubActorClaims", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitHubActorClaims_environment(ctx context.Context, field graphql.CollectedField, obj *activitylog.GitHubActorClaims) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_GitHubActorClaims_environment, + func(ctx context.Context) (any, error) { + return obj.Environment, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_GitHubActorClaims_environment(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitHubActorClaims", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitHubActorClaims_jobWorkflowRef(ctx context.Context, field graphql.CollectedField, obj *activitylog.GitHubActorClaims) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_GitHubActorClaims_jobWorkflowRef, + func(ctx context.Context) (any, error) { + return obj.JobWorkflowRef, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_GitHubActorClaims_jobWorkflowRef(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitHubActorClaims", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ResourceChangedField_field(ctx context.Context, field graphql.CollectedField, obj *activitylog.ResourceChangedField) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_ResourceChangedField_field, + func(ctx context.Context) (any, error) { + return obj.Field, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_ResourceChangedField_field(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ResourceChangedField", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ResourceChangedField_oldValue(ctx context.Context, field graphql.CollectedField, obj *activitylog.ResourceChangedField) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_ResourceChangedField_oldValue, + func(ctx context.Context) (any, error) { + return obj.OldValue, nil + }, + nil, + ec.marshalOString2ᚖstring, + true, + false, + ) +} + +func (ec *executionContext) fieldContext_ResourceChangedField_oldValue(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ResourceChangedField", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ResourceChangedField_newValue(ctx context.Context, field graphql.CollectedField, obj *activitylog.ResourceChangedField) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_ResourceChangedField_newValue, + func(ctx context.Context) (any, error) { + return obj.NewValue, nil + }, + nil, + ec.marshalOString2ᚖstring, + true, + false, + ) +} + +func (ec *executionContext) fieldContext_ResourceChangedField_newValue(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ResourceChangedField", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +// endregion **************************** field.gotpl ***************************** + +// region **************************** input.gotpl ***************************** + +// endregion **************************** input.gotpl ***************************** + +// region ************************** interface.gotpl *************************** + +// endregion ************************** interface.gotpl *************************** + +// region **************************** object.gotpl **************************** + +var genericKubernetesResourceActivityLogEntryImplementors = []string{"GenericKubernetesResourceActivityLogEntry", "ActivityLogEntry", "Node"} + +func (ec *executionContext) _GenericKubernetesResourceActivityLogEntry(ctx context.Context, sel ast.SelectionSet, obj *activitylog.GenericKubernetesResourceActivityLogEntry) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, genericKubernetesResourceActivityLogEntryImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("GenericKubernetesResourceActivityLogEntry") + case "id": + out.Values[i] = ec._GenericKubernetesResourceActivityLogEntry_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "actor": + out.Values[i] = ec._GenericKubernetesResourceActivityLogEntry_actor(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "createdAt": + out.Values[i] = ec._GenericKubernetesResourceActivityLogEntry_createdAt(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "message": + out.Values[i] = ec._GenericKubernetesResourceActivityLogEntry_message(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "resourceType": + out.Values[i] = ec._GenericKubernetesResourceActivityLogEntry_resourceType(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "resourceName": + out.Values[i] = ec._GenericKubernetesResourceActivityLogEntry_resourceName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "teamSlug": + out.Values[i] = ec._GenericKubernetesResourceActivityLogEntry_teamSlug(ctx, field, obj) + case "environmentName": + out.Values[i] = ec._GenericKubernetesResourceActivityLogEntry_environmentName(ctx, field, obj) + case "data": + out.Values[i] = ec._GenericKubernetesResourceActivityLogEntry_data(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var genericKubernetesResourceActivityLogEntryDataImplementors = []string{"GenericKubernetesResourceActivityLogEntryData"} + +func (ec *executionContext) _GenericKubernetesResourceActivityLogEntryData(ctx context.Context, sel ast.SelectionSet, obj *activitylog.GenericKubernetesResourceActivityLogEntryData) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, genericKubernetesResourceActivityLogEntryDataImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("GenericKubernetesResourceActivityLogEntryData") + case "apiVersion": + out.Values[i] = ec._GenericKubernetesResourceActivityLogEntryData_apiVersion(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "kind": + out.Values[i] = ec._GenericKubernetesResourceActivityLogEntryData_kind(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "changedFields": + out.Values[i] = ec._GenericKubernetesResourceActivityLogEntryData_changedFields(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "gitHubClaims": + out.Values[i] = ec._GenericKubernetesResourceActivityLogEntryData_gitHubClaims(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var gitHubActorClaimsImplementors = []string{"GitHubActorClaims"} + +func (ec *executionContext) _GitHubActorClaims(ctx context.Context, sel ast.SelectionSet, obj *activitylog.GitHubActorClaims) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, gitHubActorClaimsImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("GitHubActorClaims") + case "ref": + out.Values[i] = ec._GitHubActorClaims_ref(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "repository": + out.Values[i] = ec._GitHubActorClaims_repository(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "repositoryID": + out.Values[i] = ec._GitHubActorClaims_repositoryID(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "runID": + out.Values[i] = ec._GitHubActorClaims_runID(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "runAttempt": + out.Values[i] = ec._GitHubActorClaims_runAttempt(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "actor": + out.Values[i] = ec._GitHubActorClaims_actor(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "workflow": + out.Values[i] = ec._GitHubActorClaims_workflow(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "eventName": + out.Values[i] = ec._GitHubActorClaims_eventName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "environment": + out.Values[i] = ec._GitHubActorClaims_environment(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "jobWorkflowRef": + out.Values[i] = ec._GitHubActorClaims_jobWorkflowRef(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var resourceChangedFieldImplementors = []string{"ResourceChangedField"} + +func (ec *executionContext) _ResourceChangedField(ctx context.Context, sel ast.SelectionSet, obj *activitylog.ResourceChangedField) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, resourceChangedFieldImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ResourceChangedField") + case "field": + out.Values[i] = ec._ResourceChangedField_field(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "oldValue": + out.Values[i] = ec._ResourceChangedField_oldValue(ctx, field, obj) + case "newValue": + out.Values[i] = ec._ResourceChangedField_newValue(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +// endregion **************************** object.gotpl **************************** + +// region ***************************** type.gotpl ***************************** + +func (ec *executionContext) marshalNGenericKubernetesResourceActivityLogEntryData2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋactivitylogᚐGenericKubernetesResourceActivityLogEntryData(ctx context.Context, sel ast.SelectionSet, v *activitylog.GenericKubernetesResourceActivityLogEntryData) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._GenericKubernetesResourceActivityLogEntryData(ctx, sel, v) +} + +func (ec *executionContext) marshalNResourceChangedField2githubᚗcomᚋnaisᚋapiᚋinternalᚋactivitylogᚐResourceChangedField(ctx context.Context, sel ast.SelectionSet, v activitylog.ResourceChangedField) graphql.Marshaler { + return ec._ResourceChangedField(ctx, sel, &v) +} + +func (ec *executionContext) marshalNResourceChangedField2ᚕgithubᚗcomᚋnaisᚋapiᚋinternalᚋactivitylogᚐResourceChangedFieldᚄ(ctx context.Context, sel ast.SelectionSet, v []activitylog.ResourceChangedField) graphql.Marshaler { + ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler { + fc := graphql.GetFieldContext(ctx) + fc.Result = &v[i] + return ec.marshalNResourceChangedField2githubᚗcomᚋnaisᚋapiᚋinternalᚋactivitylogᚐResourceChangedField(ctx, sel, v[i]) + }) + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalOGitHubActorClaims2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋactivitylogᚐGitHubActorClaims(ctx context.Context, sel ast.SelectionSet, v *activitylog.GitHubActorClaims) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec._GitHubActorClaims(ctx, sel, v) +} + +// endregion ***************************** type.gotpl ***************************** diff --git a/internal/graph/gengql/jobs.generated.go b/internal/graph/gengql/jobs.generated.go index 52fe46270..d6b42bf9e 100644 --- a/internal/graph/gengql/jobs.generated.go +++ b/internal/graph/gengql/jobs.generated.go @@ -2201,6 +2201,277 @@ func (ec *executionContext) fieldContext_JobConnection_edges(_ context.Context, return fc, nil } +func (ec *executionContext) _JobCreatedActivityLogEntry_id(ctx context.Context, field graphql.CollectedField, obj *job.JobCreatedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_JobCreatedActivityLogEntry_id, + func(ctx context.Context) (any, error) { + return obj.ID(), nil + }, + nil, + ec.marshalNID2githubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋidentᚐIdent, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_JobCreatedActivityLogEntry_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "JobCreatedActivityLogEntry", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _JobCreatedActivityLogEntry_actor(ctx context.Context, field graphql.CollectedField, obj *job.JobCreatedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_JobCreatedActivityLogEntry_actor, + func(ctx context.Context) (any, error) { + return obj.Actor, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_JobCreatedActivityLogEntry_actor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "JobCreatedActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _JobCreatedActivityLogEntry_createdAt(ctx context.Context, field graphql.CollectedField, obj *job.JobCreatedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_JobCreatedActivityLogEntry_createdAt, + func(ctx context.Context) (any, error) { + return obj.CreatedAt, nil + }, + nil, + ec.marshalNTime2timeᚐTime, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_JobCreatedActivityLogEntry_createdAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "JobCreatedActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Time does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _JobCreatedActivityLogEntry_message(ctx context.Context, field graphql.CollectedField, obj *job.JobCreatedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_JobCreatedActivityLogEntry_message, + func(ctx context.Context) (any, error) { + return obj.Message, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_JobCreatedActivityLogEntry_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "JobCreatedActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _JobCreatedActivityLogEntry_resourceType(ctx context.Context, field graphql.CollectedField, obj *job.JobCreatedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_JobCreatedActivityLogEntry_resourceType, + func(ctx context.Context) (any, error) { + return obj.ResourceType, nil + }, + nil, + ec.marshalNActivityLogEntryResourceType2githubᚗcomᚋnaisᚋapiᚋinternalᚋactivitylogᚐActivityLogEntryResourceType, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_JobCreatedActivityLogEntry_resourceType(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "JobCreatedActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ActivityLogEntryResourceType does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _JobCreatedActivityLogEntry_resourceName(ctx context.Context, field graphql.CollectedField, obj *job.JobCreatedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_JobCreatedActivityLogEntry_resourceName, + func(ctx context.Context) (any, error) { + return obj.ResourceName, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_JobCreatedActivityLogEntry_resourceName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "JobCreatedActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _JobCreatedActivityLogEntry_teamSlug(ctx context.Context, field graphql.CollectedField, obj *job.JobCreatedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_JobCreatedActivityLogEntry_teamSlug, + func(ctx context.Context) (any, error) { + return obj.TeamSlug, nil + }, + nil, + ec.marshalNSlug2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋslugᚐSlug, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_JobCreatedActivityLogEntry_teamSlug(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "JobCreatedActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Slug does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _JobCreatedActivityLogEntry_environmentName(ctx context.Context, field graphql.CollectedField, obj *job.JobCreatedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_JobCreatedActivityLogEntry_environmentName, + func(ctx context.Context) (any, error) { + return obj.EnvironmentName, nil + }, + nil, + ec.marshalOString2ᚖstring, + true, + false, + ) +} + +func (ec *executionContext) fieldContext_JobCreatedActivityLogEntry_environmentName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "JobCreatedActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _JobCreatedActivityLogEntry_data(ctx context.Context, field graphql.CollectedField, obj *job.JobCreatedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_JobCreatedActivityLogEntry_data, + func(ctx context.Context) (any, error) { + return obj.Data, nil + }, + nil, + ec.marshalNGenericKubernetesResourceActivityLogEntryData2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋactivitylogᚐGenericKubernetesResourceActivityLogEntryData, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_JobCreatedActivityLogEntry_data(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "JobCreatedActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "apiVersion": + return ec.fieldContext_GenericKubernetesResourceActivityLogEntryData_apiVersion(ctx, field) + case "kind": + return ec.fieldContext_GenericKubernetesResourceActivityLogEntryData_kind(ctx, field) + case "changedFields": + return ec.fieldContext_GenericKubernetesResourceActivityLogEntryData_changedFields(ctx, field) + case "gitHubClaims": + return ec.fieldContext_GenericKubernetesResourceActivityLogEntryData_gitHubClaims(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type GenericKubernetesResourceActivityLogEntryData", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _JobDeletedActivityLogEntry_id(ctx context.Context, field graphql.CollectedField, obj *job.JobDeletedActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -3711,31 +3982,263 @@ func (ec *executionContext) _JobRunStatus_state(ctx context.Context, field graph return obj.State, nil }, nil, - ec.marshalNJobRunState2githubᚗcomᚋnaisᚋapiᚋinternalᚋworkloadᚋjobᚐJobRunState, + ec.marshalNJobRunState2githubᚗcomᚋnaisᚋapiᚋinternalᚋworkloadᚋjobᚐJobRunState, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_JobRunStatus_state(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "JobRunStatus", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type JobRunState does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _JobRunStatus_message(ctx context.Context, field graphql.CollectedField, obj *job.JobRunStatus) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_JobRunStatus_message, + func(ctx context.Context) (any, error) { + return obj.Message, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_JobRunStatus_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "JobRunStatus", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _JobRunTrigger_type(ctx context.Context, field graphql.CollectedField, obj *job.JobRunTrigger) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_JobRunTrigger_type, + func(ctx context.Context) (any, error) { + return obj.Type, nil + }, + nil, + ec.marshalNJobRunTriggerType2githubᚗcomᚋnaisᚋapiᚋinternalᚋworkloadᚋjobᚐJobRunTriggerType, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_JobRunTrigger_type(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "JobRunTrigger", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type JobRunTriggerType does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _JobRunTrigger_actor(ctx context.Context, field graphql.CollectedField, obj *job.JobRunTrigger) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_JobRunTrigger_actor, + func(ctx context.Context) (any, error) { + return obj.Actor, nil + }, + nil, + ec.marshalOString2ᚖstring, + true, + false, + ) +} + +func (ec *executionContext) fieldContext_JobRunTrigger_actor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "JobRunTrigger", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _JobSchedule_expression(ctx context.Context, field graphql.CollectedField, obj *job.JobSchedule) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_JobSchedule_expression, + func(ctx context.Context) (any, error) { + return obj.Expression, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_JobSchedule_expression(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "JobSchedule", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _JobSchedule_timeZone(ctx context.Context, field graphql.CollectedField, obj *job.JobSchedule) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_JobSchedule_timeZone, + func(ctx context.Context) (any, error) { + return obj.TimeZone, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_JobSchedule_timeZone(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "JobSchedule", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _JobTriggeredActivityLogEntry_id(ctx context.Context, field graphql.CollectedField, obj *job.JobTriggeredActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_JobTriggeredActivityLogEntry_id, + func(ctx context.Context) (any, error) { + return obj.ID(), nil + }, + nil, + ec.marshalNID2githubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋidentᚐIdent, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_JobTriggeredActivityLogEntry_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "JobTriggeredActivityLogEntry", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _JobTriggeredActivityLogEntry_actor(ctx context.Context, field graphql.CollectedField, obj *job.JobTriggeredActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_JobTriggeredActivityLogEntry_actor, + func(ctx context.Context) (any, error) { + return obj.Actor, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_JobTriggeredActivityLogEntry_actor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "JobTriggeredActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _JobTriggeredActivityLogEntry_createdAt(ctx context.Context, field graphql.CollectedField, obj *job.JobTriggeredActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_JobTriggeredActivityLogEntry_createdAt, + func(ctx context.Context) (any, error) { + return obj.CreatedAt, nil + }, + nil, + ec.marshalNTime2timeᚐTime, true, true, ) } -func (ec *executionContext) fieldContext_JobRunStatus_state(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_JobTriggeredActivityLogEntry_createdAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "JobRunStatus", + Object: "JobTriggeredActivityLogEntry", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type JobRunState does not have child fields") + return nil, errors.New("field of type Time does not have child fields") }, } return fc, nil } -func (ec *executionContext) _JobRunStatus_message(ctx context.Context, field graphql.CollectedField, obj *job.JobRunStatus) (ret graphql.Marshaler) { +func (ec *executionContext) _JobTriggeredActivityLogEntry_message(ctx context.Context, field graphql.CollectedField, obj *job.JobTriggeredActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, - ec.fieldContext_JobRunStatus_message, + ec.fieldContext_JobTriggeredActivityLogEntry_message, func(ctx context.Context) (any, error) { return obj.Message, nil }, @@ -3746,9 +4249,9 @@ func (ec *executionContext) _JobRunStatus_message(ctx context.Context, field gra ) } -func (ec *executionContext) fieldContext_JobRunStatus_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_JobTriggeredActivityLogEntry_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "JobRunStatus", + Object: "JobTriggeredActivityLogEntry", Field: field, IsMethod: false, IsResolver: false, @@ -3759,54 +4262,54 @@ func (ec *executionContext) fieldContext_JobRunStatus_message(_ context.Context, return fc, nil } -func (ec *executionContext) _JobRunTrigger_type(ctx context.Context, field graphql.CollectedField, obj *job.JobRunTrigger) (ret graphql.Marshaler) { +func (ec *executionContext) _JobTriggeredActivityLogEntry_resourceType(ctx context.Context, field graphql.CollectedField, obj *job.JobTriggeredActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, - ec.fieldContext_JobRunTrigger_type, + ec.fieldContext_JobTriggeredActivityLogEntry_resourceType, func(ctx context.Context) (any, error) { - return obj.Type, nil + return obj.ResourceType, nil }, nil, - ec.marshalNJobRunTriggerType2githubᚗcomᚋnaisᚋapiᚋinternalᚋworkloadᚋjobᚐJobRunTriggerType, + ec.marshalNActivityLogEntryResourceType2githubᚗcomᚋnaisᚋapiᚋinternalᚋactivitylogᚐActivityLogEntryResourceType, true, true, ) } -func (ec *executionContext) fieldContext_JobRunTrigger_type(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_JobTriggeredActivityLogEntry_resourceType(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "JobRunTrigger", + Object: "JobTriggeredActivityLogEntry", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type JobRunTriggerType does not have child fields") + return nil, errors.New("field of type ActivityLogEntryResourceType does not have child fields") }, } return fc, nil } -func (ec *executionContext) _JobRunTrigger_actor(ctx context.Context, field graphql.CollectedField, obj *job.JobRunTrigger) (ret graphql.Marshaler) { +func (ec *executionContext) _JobTriggeredActivityLogEntry_resourceName(ctx context.Context, field graphql.CollectedField, obj *job.JobTriggeredActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, - ec.fieldContext_JobRunTrigger_actor, + ec.fieldContext_JobTriggeredActivityLogEntry_resourceName, func(ctx context.Context) (any, error) { - return obj.Actor, nil + return obj.ResourceName, nil }, nil, - ec.marshalOString2ᚖstring, + ec.marshalNString2string, + true, true, - false, ) } -func (ec *executionContext) fieldContext_JobRunTrigger_actor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_JobTriggeredActivityLogEntry_resourceName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "JobRunTrigger", + Object: "JobTriggeredActivityLogEntry", Field: field, IsMethod: false, IsResolver: false, @@ -3817,54 +4320,54 @@ func (ec *executionContext) fieldContext_JobRunTrigger_actor(_ context.Context, return fc, nil } -func (ec *executionContext) _JobSchedule_expression(ctx context.Context, field graphql.CollectedField, obj *job.JobSchedule) (ret graphql.Marshaler) { +func (ec *executionContext) _JobTriggeredActivityLogEntry_teamSlug(ctx context.Context, field graphql.CollectedField, obj *job.JobTriggeredActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, - ec.fieldContext_JobSchedule_expression, + ec.fieldContext_JobTriggeredActivityLogEntry_teamSlug, func(ctx context.Context) (any, error) { - return obj.Expression, nil + return obj.TeamSlug, nil }, nil, - ec.marshalNString2string, + ec.marshalNSlug2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋslugᚐSlug, true, true, ) } -func (ec *executionContext) fieldContext_JobSchedule_expression(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_JobTriggeredActivityLogEntry_teamSlug(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "JobSchedule", + Object: "JobTriggeredActivityLogEntry", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type Slug does not have child fields") }, } return fc, nil } -func (ec *executionContext) _JobSchedule_timeZone(ctx context.Context, field graphql.CollectedField, obj *job.JobSchedule) (ret graphql.Marshaler) { +func (ec *executionContext) _JobTriggeredActivityLogEntry_environmentName(ctx context.Context, field graphql.CollectedField, obj *job.JobTriggeredActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, - ec.fieldContext_JobSchedule_timeZone, + ec.fieldContext_JobTriggeredActivityLogEntry_environmentName, func(ctx context.Context) (any, error) { - return obj.TimeZone, nil + return obj.EnvironmentName, nil }, nil, - ec.marshalNString2string, - true, + ec.marshalOString2ᚖstring, true, + false, ) } -func (ec *executionContext) fieldContext_JobSchedule_timeZone(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_JobTriggeredActivityLogEntry_environmentName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "JobSchedule", + Object: "JobTriggeredActivityLogEntry", Field: field, IsMethod: false, IsResolver: false, @@ -3875,12 +4378,12 @@ func (ec *executionContext) fieldContext_JobSchedule_timeZone(_ context.Context, return fc, nil } -func (ec *executionContext) _JobTriggeredActivityLogEntry_id(ctx context.Context, field graphql.CollectedField, obj *job.JobTriggeredActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _JobUpdatedActivityLogEntry_id(ctx context.Context, field graphql.CollectedField, obj *job.JobUpdatedActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, - ec.fieldContext_JobTriggeredActivityLogEntry_id, + ec.fieldContext_JobUpdatedActivityLogEntry_id, func(ctx context.Context) (any, error) { return obj.ID(), nil }, @@ -3891,9 +4394,9 @@ func (ec *executionContext) _JobTriggeredActivityLogEntry_id(ctx context.Context ) } -func (ec *executionContext) fieldContext_JobTriggeredActivityLogEntry_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_JobUpdatedActivityLogEntry_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "JobTriggeredActivityLogEntry", + Object: "JobUpdatedActivityLogEntry", Field: field, IsMethod: true, IsResolver: false, @@ -3904,12 +4407,12 @@ func (ec *executionContext) fieldContext_JobTriggeredActivityLogEntry_id(_ conte return fc, nil } -func (ec *executionContext) _JobTriggeredActivityLogEntry_actor(ctx context.Context, field graphql.CollectedField, obj *job.JobTriggeredActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _JobUpdatedActivityLogEntry_actor(ctx context.Context, field graphql.CollectedField, obj *job.JobUpdatedActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, - ec.fieldContext_JobTriggeredActivityLogEntry_actor, + ec.fieldContext_JobUpdatedActivityLogEntry_actor, func(ctx context.Context) (any, error) { return obj.Actor, nil }, @@ -3920,9 +4423,9 @@ func (ec *executionContext) _JobTriggeredActivityLogEntry_actor(ctx context.Cont ) } -func (ec *executionContext) fieldContext_JobTriggeredActivityLogEntry_actor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_JobUpdatedActivityLogEntry_actor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "JobTriggeredActivityLogEntry", + Object: "JobUpdatedActivityLogEntry", Field: field, IsMethod: false, IsResolver: false, @@ -3933,12 +4436,12 @@ func (ec *executionContext) fieldContext_JobTriggeredActivityLogEntry_actor(_ co return fc, nil } -func (ec *executionContext) _JobTriggeredActivityLogEntry_createdAt(ctx context.Context, field graphql.CollectedField, obj *job.JobTriggeredActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _JobUpdatedActivityLogEntry_createdAt(ctx context.Context, field graphql.CollectedField, obj *job.JobUpdatedActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, - ec.fieldContext_JobTriggeredActivityLogEntry_createdAt, + ec.fieldContext_JobUpdatedActivityLogEntry_createdAt, func(ctx context.Context) (any, error) { return obj.CreatedAt, nil }, @@ -3949,9 +4452,9 @@ func (ec *executionContext) _JobTriggeredActivityLogEntry_createdAt(ctx context. ) } -func (ec *executionContext) fieldContext_JobTriggeredActivityLogEntry_createdAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_JobUpdatedActivityLogEntry_createdAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "JobTriggeredActivityLogEntry", + Object: "JobUpdatedActivityLogEntry", Field: field, IsMethod: false, IsResolver: false, @@ -3962,12 +4465,12 @@ func (ec *executionContext) fieldContext_JobTriggeredActivityLogEntry_createdAt( return fc, nil } -func (ec *executionContext) _JobTriggeredActivityLogEntry_message(ctx context.Context, field graphql.CollectedField, obj *job.JobTriggeredActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _JobUpdatedActivityLogEntry_message(ctx context.Context, field graphql.CollectedField, obj *job.JobUpdatedActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, - ec.fieldContext_JobTriggeredActivityLogEntry_message, + ec.fieldContext_JobUpdatedActivityLogEntry_message, func(ctx context.Context) (any, error) { return obj.Message, nil }, @@ -3978,9 +4481,9 @@ func (ec *executionContext) _JobTriggeredActivityLogEntry_message(ctx context.Co ) } -func (ec *executionContext) fieldContext_JobTriggeredActivityLogEntry_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_JobUpdatedActivityLogEntry_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "JobTriggeredActivityLogEntry", + Object: "JobUpdatedActivityLogEntry", Field: field, IsMethod: false, IsResolver: false, @@ -3991,12 +4494,12 @@ func (ec *executionContext) fieldContext_JobTriggeredActivityLogEntry_message(_ return fc, nil } -func (ec *executionContext) _JobTriggeredActivityLogEntry_resourceType(ctx context.Context, field graphql.CollectedField, obj *job.JobTriggeredActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _JobUpdatedActivityLogEntry_resourceType(ctx context.Context, field graphql.CollectedField, obj *job.JobUpdatedActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, - ec.fieldContext_JobTriggeredActivityLogEntry_resourceType, + ec.fieldContext_JobUpdatedActivityLogEntry_resourceType, func(ctx context.Context) (any, error) { return obj.ResourceType, nil }, @@ -4007,9 +4510,9 @@ func (ec *executionContext) _JobTriggeredActivityLogEntry_resourceType(ctx conte ) } -func (ec *executionContext) fieldContext_JobTriggeredActivityLogEntry_resourceType(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_JobUpdatedActivityLogEntry_resourceType(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "JobTriggeredActivityLogEntry", + Object: "JobUpdatedActivityLogEntry", Field: field, IsMethod: false, IsResolver: false, @@ -4020,12 +4523,12 @@ func (ec *executionContext) fieldContext_JobTriggeredActivityLogEntry_resourceTy return fc, nil } -func (ec *executionContext) _JobTriggeredActivityLogEntry_resourceName(ctx context.Context, field graphql.CollectedField, obj *job.JobTriggeredActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _JobUpdatedActivityLogEntry_resourceName(ctx context.Context, field graphql.CollectedField, obj *job.JobUpdatedActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, - ec.fieldContext_JobTriggeredActivityLogEntry_resourceName, + ec.fieldContext_JobUpdatedActivityLogEntry_resourceName, func(ctx context.Context) (any, error) { return obj.ResourceName, nil }, @@ -4036,9 +4539,9 @@ func (ec *executionContext) _JobTriggeredActivityLogEntry_resourceName(ctx conte ) } -func (ec *executionContext) fieldContext_JobTriggeredActivityLogEntry_resourceName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_JobUpdatedActivityLogEntry_resourceName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "JobTriggeredActivityLogEntry", + Object: "JobUpdatedActivityLogEntry", Field: field, IsMethod: false, IsResolver: false, @@ -4049,12 +4552,12 @@ func (ec *executionContext) fieldContext_JobTriggeredActivityLogEntry_resourceNa return fc, nil } -func (ec *executionContext) _JobTriggeredActivityLogEntry_teamSlug(ctx context.Context, field graphql.CollectedField, obj *job.JobTriggeredActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _JobUpdatedActivityLogEntry_teamSlug(ctx context.Context, field graphql.CollectedField, obj *job.JobUpdatedActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, - ec.fieldContext_JobTriggeredActivityLogEntry_teamSlug, + ec.fieldContext_JobUpdatedActivityLogEntry_teamSlug, func(ctx context.Context) (any, error) { return obj.TeamSlug, nil }, @@ -4065,9 +4568,9 @@ func (ec *executionContext) _JobTriggeredActivityLogEntry_teamSlug(ctx context.C ) } -func (ec *executionContext) fieldContext_JobTriggeredActivityLogEntry_teamSlug(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_JobUpdatedActivityLogEntry_teamSlug(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "JobTriggeredActivityLogEntry", + Object: "JobUpdatedActivityLogEntry", Field: field, IsMethod: false, IsResolver: false, @@ -4078,12 +4581,12 @@ func (ec *executionContext) fieldContext_JobTriggeredActivityLogEntry_teamSlug(_ return fc, nil } -func (ec *executionContext) _JobTriggeredActivityLogEntry_environmentName(ctx context.Context, field graphql.CollectedField, obj *job.JobTriggeredActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _JobUpdatedActivityLogEntry_environmentName(ctx context.Context, field graphql.CollectedField, obj *job.JobUpdatedActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, - ec.fieldContext_JobTriggeredActivityLogEntry_environmentName, + ec.fieldContext_JobUpdatedActivityLogEntry_environmentName, func(ctx context.Context) (any, error) { return obj.EnvironmentName, nil }, @@ -4094,9 +4597,9 @@ func (ec *executionContext) _JobTriggeredActivityLogEntry_environmentName(ctx co ) } -func (ec *executionContext) fieldContext_JobTriggeredActivityLogEntry_environmentName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_JobUpdatedActivityLogEntry_environmentName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "JobTriggeredActivityLogEntry", + Object: "JobUpdatedActivityLogEntry", Field: field, IsMethod: false, IsResolver: false, @@ -4107,6 +4610,45 @@ func (ec *executionContext) fieldContext_JobTriggeredActivityLogEntry_environmen return fc, nil } +func (ec *executionContext) _JobUpdatedActivityLogEntry_data(ctx context.Context, field graphql.CollectedField, obj *job.JobUpdatedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_JobUpdatedActivityLogEntry_data, + func(ctx context.Context) (any, error) { + return obj.Data, nil + }, + nil, + ec.marshalNGenericKubernetesResourceActivityLogEntryData2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋactivitylogᚐGenericKubernetesResourceActivityLogEntryData, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_JobUpdatedActivityLogEntry_data(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "JobUpdatedActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "apiVersion": + return ec.fieldContext_GenericKubernetesResourceActivityLogEntryData_apiVersion(ctx, field) + case "kind": + return ec.fieldContext_GenericKubernetesResourceActivityLogEntryData_kind(ctx, field) + case "changedFields": + return ec.fieldContext_GenericKubernetesResourceActivityLogEntryData_changedFields(ctx, field) + case "gitHubClaims": + return ec.fieldContext_GenericKubernetesResourceActivityLogEntryData_gitHubClaims(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type GenericKubernetesResourceActivityLogEntryData", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _TeamInventoryCountJobs_total(ctx context.Context, field graphql.CollectedField, obj *job.TeamInventoryCountJobs) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -5617,6 +6159,82 @@ func (ec *executionContext) _JobConnection(ctx context.Context, sel ast.Selectio return out } +var jobCreatedActivityLogEntryImplementors = []string{"JobCreatedActivityLogEntry", "ActivityLogEntry", "Node"} + +func (ec *executionContext) _JobCreatedActivityLogEntry(ctx context.Context, sel ast.SelectionSet, obj *job.JobCreatedActivityLogEntry) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, jobCreatedActivityLogEntryImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("JobCreatedActivityLogEntry") + case "id": + out.Values[i] = ec._JobCreatedActivityLogEntry_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "actor": + out.Values[i] = ec._JobCreatedActivityLogEntry_actor(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "createdAt": + out.Values[i] = ec._JobCreatedActivityLogEntry_createdAt(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "message": + out.Values[i] = ec._JobCreatedActivityLogEntry_message(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "resourceType": + out.Values[i] = ec._JobCreatedActivityLogEntry_resourceType(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "resourceName": + out.Values[i] = ec._JobCreatedActivityLogEntry_resourceName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "teamSlug": + out.Values[i] = ec._JobCreatedActivityLogEntry_teamSlug(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "environmentName": + out.Values[i] = ec._JobCreatedActivityLogEntry_environmentName(ctx, field, obj) + case "data": + out.Values[i] = ec._JobCreatedActivityLogEntry_data(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var jobDeletedActivityLogEntryImplementors = []string{"JobDeletedActivityLogEntry", "ActivityLogEntry", "Node"} func (ec *executionContext) _JobDeletedActivityLogEntry(ctx context.Context, sel ast.SelectionSet, obj *job.JobDeletedActivityLogEntry) graphql.Marshaler { @@ -6492,6 +7110,82 @@ func (ec *executionContext) _JobTriggeredActivityLogEntry(ctx context.Context, s return out } +var jobUpdatedActivityLogEntryImplementors = []string{"JobUpdatedActivityLogEntry", "ActivityLogEntry", "Node"} + +func (ec *executionContext) _JobUpdatedActivityLogEntry(ctx context.Context, sel ast.SelectionSet, obj *job.JobUpdatedActivityLogEntry) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, jobUpdatedActivityLogEntryImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("JobUpdatedActivityLogEntry") + case "id": + out.Values[i] = ec._JobUpdatedActivityLogEntry_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "actor": + out.Values[i] = ec._JobUpdatedActivityLogEntry_actor(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "createdAt": + out.Values[i] = ec._JobUpdatedActivityLogEntry_createdAt(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "message": + out.Values[i] = ec._JobUpdatedActivityLogEntry_message(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "resourceType": + out.Values[i] = ec._JobUpdatedActivityLogEntry_resourceType(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "resourceName": + out.Values[i] = ec._JobUpdatedActivityLogEntry_resourceName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "teamSlug": + out.Values[i] = ec._JobUpdatedActivityLogEntry_teamSlug(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "environmentName": + out.Values[i] = ec._JobUpdatedActivityLogEntry_environmentName(ctx, field, obj) + case "data": + out.Values[i] = ec._JobUpdatedActivityLogEntry_data(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var teamInventoryCountJobsImplementors = []string{"TeamInventoryCountJobs"} func (ec *executionContext) _TeamInventoryCountJobs(ctx context.Context, sel ast.SelectionSet, obj *job.TeamInventoryCountJobs) graphql.Marshaler { diff --git a/internal/graph/gengql/root_.generated.go b/internal/graph/gengql/root_.generated.go index 1005e4cc4..6b7cb5506 100644 --- a/internal/graph/gengql/root_.generated.go +++ b/internal/graph/gengql/root_.generated.go @@ -222,6 +222,18 @@ type ComplexityRoot struct { PageInfo func(childComplexity int) int } + ApplicationCreatedActivityLogEntry struct { + Actor func(childComplexity int) int + CreatedAt func(childComplexity int) int + Data func(childComplexity int) int + EnvironmentName func(childComplexity int) int + ID func(childComplexity int) int + Message func(childComplexity int) int + ResourceName func(childComplexity int) int + ResourceType func(childComplexity int) int + TeamSlug func(childComplexity int) int + } + ApplicationDeletedActivityLogEntry struct { Actor func(childComplexity int) int CreatedAt func(childComplexity int) int @@ -312,6 +324,18 @@ type ComplexityRoot struct { Strategies func(childComplexity int) int } + ApplicationUpdatedActivityLogEntry struct { + Actor func(childComplexity int) int + CreatedAt func(childComplexity int) int + Data func(childComplexity int) int + EnvironmentName func(childComplexity int) int + ID func(childComplexity int) int + Message func(childComplexity int) int + ResourceName func(childComplexity int) int + ResourceType func(childComplexity int) int + TeamSlug func(childComplexity int) int + } + AssignRoleToServiceAccountPayload struct { ServiceAccount func(childComplexity int) int } @@ -839,6 +863,38 @@ type ComplexityRoot struct { Valkey func(childComplexity int) int } + GenericKubernetesResourceActivityLogEntry struct { + Actor func(childComplexity int) int + CreatedAt func(childComplexity int) int + Data func(childComplexity int) int + EnvironmentName func(childComplexity int) int + ID func(childComplexity int) int + Message func(childComplexity int) int + ResourceName func(childComplexity int) int + ResourceType func(childComplexity int) int + TeamSlug func(childComplexity int) int + } + + GenericKubernetesResourceActivityLogEntryData struct { + APIVersion func(childComplexity int) int + ChangedFields func(childComplexity int) int + GitHubClaims func(childComplexity int) int + Kind func(childComplexity int) int + } + + GitHubActorClaims struct { + Actor func(childComplexity int) int + Environment func(childComplexity int) int + EventName func(childComplexity int) int + JobWorkflowRef func(childComplexity int) int + Ref func(childComplexity int) int + Repository func(childComplexity int) int + RepositoryID func(childComplexity int) int + RunAttempt func(childComplexity int) int + RunID func(childComplexity int) int + Workflow func(childComplexity int) int + } + GrantPostgresAccessPayload struct { Error func(childComplexity int) int } @@ -974,6 +1030,18 @@ type ComplexityRoot struct { PageInfo func(childComplexity int) int } + JobCreatedActivityLogEntry struct { + Actor func(childComplexity int) int + CreatedAt func(childComplexity int) int + Data func(childComplexity int) int + EnvironmentName func(childComplexity int) int + ID func(childComplexity int) int + Message func(childComplexity int) int + ResourceName func(childComplexity int) int + ResourceType func(childComplexity int) int + TeamSlug func(childComplexity int) int + } + JobDeletedActivityLogEntry struct { Actor func(childComplexity int) int CreatedAt func(childComplexity int) int @@ -1080,6 +1148,18 @@ type ComplexityRoot struct { TeamSlug func(childComplexity int) int } + JobUpdatedActivityLogEntry struct { + Actor func(childComplexity int) int + CreatedAt func(childComplexity int) int + Data func(childComplexity int) int + EnvironmentName func(childComplexity int) int + ID func(childComplexity int) int + Message func(childComplexity int) int + ResourceName func(childComplexity int) int + ResourceType func(childComplexity int) int + TeamSlug func(childComplexity int) int + } + KafkaCredentials struct { AccessCert func(childComplexity int) int AccessKey func(childComplexity int) int @@ -1715,6 +1795,12 @@ type ComplexityRoot struct { Key func(childComplexity int) int } + ResourceChangedField struct { + Field func(childComplexity int) int + NewValue func(childComplexity int) int + OldValue func(childComplexity int) int + } + RestartApplicationPayload struct { Application func(childComplexity int) int } @@ -3580,6 +3666,69 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.ApplicationConnection.PageInfo(childComplexity), true + case "ApplicationCreatedActivityLogEntry.actor": + if e.ComplexityRoot.ApplicationCreatedActivityLogEntry.Actor == nil { + break + } + + return e.ComplexityRoot.ApplicationCreatedActivityLogEntry.Actor(childComplexity), true + + case "ApplicationCreatedActivityLogEntry.createdAt": + if e.ComplexityRoot.ApplicationCreatedActivityLogEntry.CreatedAt == nil { + break + } + + return e.ComplexityRoot.ApplicationCreatedActivityLogEntry.CreatedAt(childComplexity), true + + case "ApplicationCreatedActivityLogEntry.data": + if e.ComplexityRoot.ApplicationCreatedActivityLogEntry.Data == nil { + break + } + + return e.ComplexityRoot.ApplicationCreatedActivityLogEntry.Data(childComplexity), true + + case "ApplicationCreatedActivityLogEntry.environmentName": + if e.ComplexityRoot.ApplicationCreatedActivityLogEntry.EnvironmentName == nil { + break + } + + return e.ComplexityRoot.ApplicationCreatedActivityLogEntry.EnvironmentName(childComplexity), true + + case "ApplicationCreatedActivityLogEntry.id": + if e.ComplexityRoot.ApplicationCreatedActivityLogEntry.ID == nil { + break + } + + return e.ComplexityRoot.ApplicationCreatedActivityLogEntry.ID(childComplexity), true + + case "ApplicationCreatedActivityLogEntry.message": + if e.ComplexityRoot.ApplicationCreatedActivityLogEntry.Message == nil { + break + } + + return e.ComplexityRoot.ApplicationCreatedActivityLogEntry.Message(childComplexity), true + + case "ApplicationCreatedActivityLogEntry.resourceName": + if e.ComplexityRoot.ApplicationCreatedActivityLogEntry.ResourceName == nil { + break + } + + return e.ComplexityRoot.ApplicationCreatedActivityLogEntry.ResourceName(childComplexity), true + + case "ApplicationCreatedActivityLogEntry.resourceType": + if e.ComplexityRoot.ApplicationCreatedActivityLogEntry.ResourceType == nil { + break + } + + return e.ComplexityRoot.ApplicationCreatedActivityLogEntry.ResourceType(childComplexity), true + + case "ApplicationCreatedActivityLogEntry.teamSlug": + if e.ComplexityRoot.ApplicationCreatedActivityLogEntry.TeamSlug == nil { + break + } + + return e.ComplexityRoot.ApplicationCreatedActivityLogEntry.TeamSlug(childComplexity), true + case "ApplicationDeletedActivityLogEntry.actor": if e.ComplexityRoot.ApplicationDeletedActivityLogEntry.Actor == nil { break @@ -3942,6 +4091,69 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.ApplicationScaling.Strategies(childComplexity), true + case "ApplicationUpdatedActivityLogEntry.actor": + if e.ComplexityRoot.ApplicationUpdatedActivityLogEntry.Actor == nil { + break + } + + return e.ComplexityRoot.ApplicationUpdatedActivityLogEntry.Actor(childComplexity), true + + case "ApplicationUpdatedActivityLogEntry.createdAt": + if e.ComplexityRoot.ApplicationUpdatedActivityLogEntry.CreatedAt == nil { + break + } + + return e.ComplexityRoot.ApplicationUpdatedActivityLogEntry.CreatedAt(childComplexity), true + + case "ApplicationUpdatedActivityLogEntry.data": + if e.ComplexityRoot.ApplicationUpdatedActivityLogEntry.Data == nil { + break + } + + return e.ComplexityRoot.ApplicationUpdatedActivityLogEntry.Data(childComplexity), true + + case "ApplicationUpdatedActivityLogEntry.environmentName": + if e.ComplexityRoot.ApplicationUpdatedActivityLogEntry.EnvironmentName == nil { + break + } + + return e.ComplexityRoot.ApplicationUpdatedActivityLogEntry.EnvironmentName(childComplexity), true + + case "ApplicationUpdatedActivityLogEntry.id": + if e.ComplexityRoot.ApplicationUpdatedActivityLogEntry.ID == nil { + break + } + + return e.ComplexityRoot.ApplicationUpdatedActivityLogEntry.ID(childComplexity), true + + case "ApplicationUpdatedActivityLogEntry.message": + if e.ComplexityRoot.ApplicationUpdatedActivityLogEntry.Message == nil { + break + } + + return e.ComplexityRoot.ApplicationUpdatedActivityLogEntry.Message(childComplexity), true + + case "ApplicationUpdatedActivityLogEntry.resourceName": + if e.ComplexityRoot.ApplicationUpdatedActivityLogEntry.ResourceName == nil { + break + } + + return e.ComplexityRoot.ApplicationUpdatedActivityLogEntry.ResourceName(childComplexity), true + + case "ApplicationUpdatedActivityLogEntry.resourceType": + if e.ComplexityRoot.ApplicationUpdatedActivityLogEntry.ResourceType == nil { + break + } + + return e.ComplexityRoot.ApplicationUpdatedActivityLogEntry.ResourceType(childComplexity), true + + case "ApplicationUpdatedActivityLogEntry.teamSlug": + if e.ComplexityRoot.ApplicationUpdatedActivityLogEntry.TeamSlug == nil { + break + } + + return e.ComplexityRoot.ApplicationUpdatedActivityLogEntry.TeamSlug(childComplexity), true + case "AssignRoleToServiceAccountPayload.serviceAccount": if e.ComplexityRoot.AssignRoleToServiceAccountPayload.ServiceAccount == nil { break @@ -5890,6 +6102,167 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.Features.Valkey(childComplexity), true + case "GenericKubernetesResourceActivityLogEntry.actor": + if e.ComplexityRoot.GenericKubernetesResourceActivityLogEntry.Actor == nil { + break + } + + return e.ComplexityRoot.GenericKubernetesResourceActivityLogEntry.Actor(childComplexity), true + + case "GenericKubernetesResourceActivityLogEntry.createdAt": + if e.ComplexityRoot.GenericKubernetesResourceActivityLogEntry.CreatedAt == nil { + break + } + + return e.ComplexityRoot.GenericKubernetesResourceActivityLogEntry.CreatedAt(childComplexity), true + + case "GenericKubernetesResourceActivityLogEntry.data": + if e.ComplexityRoot.GenericKubernetesResourceActivityLogEntry.Data == nil { + break + } + + return e.ComplexityRoot.GenericKubernetesResourceActivityLogEntry.Data(childComplexity), true + + case "GenericKubernetesResourceActivityLogEntry.environmentName": + if e.ComplexityRoot.GenericKubernetesResourceActivityLogEntry.EnvironmentName == nil { + break + } + + return e.ComplexityRoot.GenericKubernetesResourceActivityLogEntry.EnvironmentName(childComplexity), true + + case "GenericKubernetesResourceActivityLogEntry.id": + if e.ComplexityRoot.GenericKubernetesResourceActivityLogEntry.ID == nil { + break + } + + return e.ComplexityRoot.GenericKubernetesResourceActivityLogEntry.ID(childComplexity), true + + case "GenericKubernetesResourceActivityLogEntry.message": + if e.ComplexityRoot.GenericKubernetesResourceActivityLogEntry.Message == nil { + break + } + + return e.ComplexityRoot.GenericKubernetesResourceActivityLogEntry.Message(childComplexity), true + + case "GenericKubernetesResourceActivityLogEntry.resourceName": + if e.ComplexityRoot.GenericKubernetesResourceActivityLogEntry.ResourceName == nil { + break + } + + return e.ComplexityRoot.GenericKubernetesResourceActivityLogEntry.ResourceName(childComplexity), true + + case "GenericKubernetesResourceActivityLogEntry.resourceType": + if e.ComplexityRoot.GenericKubernetesResourceActivityLogEntry.ResourceType == nil { + break + } + + return e.ComplexityRoot.GenericKubernetesResourceActivityLogEntry.ResourceType(childComplexity), true + + case "GenericKubernetesResourceActivityLogEntry.teamSlug": + if e.ComplexityRoot.GenericKubernetesResourceActivityLogEntry.TeamSlug == nil { + break + } + + return e.ComplexityRoot.GenericKubernetesResourceActivityLogEntry.TeamSlug(childComplexity), true + + case "GenericKubernetesResourceActivityLogEntryData.apiVersion": + if e.ComplexityRoot.GenericKubernetesResourceActivityLogEntryData.APIVersion == nil { + break + } + + return e.ComplexityRoot.GenericKubernetesResourceActivityLogEntryData.APIVersion(childComplexity), true + + case "GenericKubernetesResourceActivityLogEntryData.changedFields": + if e.ComplexityRoot.GenericKubernetesResourceActivityLogEntryData.ChangedFields == nil { + break + } + + return e.ComplexityRoot.GenericKubernetesResourceActivityLogEntryData.ChangedFields(childComplexity), true + + case "GenericKubernetesResourceActivityLogEntryData.gitHubClaims": + if e.ComplexityRoot.GenericKubernetesResourceActivityLogEntryData.GitHubClaims == nil { + break + } + + return e.ComplexityRoot.GenericKubernetesResourceActivityLogEntryData.GitHubClaims(childComplexity), true + + case "GenericKubernetesResourceActivityLogEntryData.kind": + if e.ComplexityRoot.GenericKubernetesResourceActivityLogEntryData.Kind == nil { + break + } + + return e.ComplexityRoot.GenericKubernetesResourceActivityLogEntryData.Kind(childComplexity), true + + case "GitHubActorClaims.actor": + if e.ComplexityRoot.GitHubActorClaims.Actor == nil { + break + } + + return e.ComplexityRoot.GitHubActorClaims.Actor(childComplexity), true + + case "GitHubActorClaims.environment": + if e.ComplexityRoot.GitHubActorClaims.Environment == nil { + break + } + + return e.ComplexityRoot.GitHubActorClaims.Environment(childComplexity), true + + case "GitHubActorClaims.eventName": + if e.ComplexityRoot.GitHubActorClaims.EventName == nil { + break + } + + return e.ComplexityRoot.GitHubActorClaims.EventName(childComplexity), true + + case "GitHubActorClaims.jobWorkflowRef": + if e.ComplexityRoot.GitHubActorClaims.JobWorkflowRef == nil { + break + } + + return e.ComplexityRoot.GitHubActorClaims.JobWorkflowRef(childComplexity), true + + case "GitHubActorClaims.ref": + if e.ComplexityRoot.GitHubActorClaims.Ref == nil { + break + } + + return e.ComplexityRoot.GitHubActorClaims.Ref(childComplexity), true + + case "GitHubActorClaims.repository": + if e.ComplexityRoot.GitHubActorClaims.Repository == nil { + break + } + + return e.ComplexityRoot.GitHubActorClaims.Repository(childComplexity), true + + case "GitHubActorClaims.repositoryID": + if e.ComplexityRoot.GitHubActorClaims.RepositoryID == nil { + break + } + + return e.ComplexityRoot.GitHubActorClaims.RepositoryID(childComplexity), true + + case "GitHubActorClaims.runAttempt": + if e.ComplexityRoot.GitHubActorClaims.RunAttempt == nil { + break + } + + return e.ComplexityRoot.GitHubActorClaims.RunAttempt(childComplexity), true + + case "GitHubActorClaims.runID": + if e.ComplexityRoot.GitHubActorClaims.RunID == nil { + break + } + + return e.ComplexityRoot.GitHubActorClaims.RunID(childComplexity), true + + case "GitHubActorClaims.workflow": + if e.ComplexityRoot.GitHubActorClaims.Workflow == nil { + break + } + + return e.ComplexityRoot.GitHubActorClaims.Workflow(childComplexity), true + case "GrantPostgresAccessPayload.error": if e.ComplexityRoot.GrantPostgresAccessPayload.Error == nil { break @@ -6532,6 +6905,69 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.JobConnection.PageInfo(childComplexity), true + case "JobCreatedActivityLogEntry.actor": + if e.ComplexityRoot.JobCreatedActivityLogEntry.Actor == nil { + break + } + + return e.ComplexityRoot.JobCreatedActivityLogEntry.Actor(childComplexity), true + + case "JobCreatedActivityLogEntry.createdAt": + if e.ComplexityRoot.JobCreatedActivityLogEntry.CreatedAt == nil { + break + } + + return e.ComplexityRoot.JobCreatedActivityLogEntry.CreatedAt(childComplexity), true + + case "JobCreatedActivityLogEntry.data": + if e.ComplexityRoot.JobCreatedActivityLogEntry.Data == nil { + break + } + + return e.ComplexityRoot.JobCreatedActivityLogEntry.Data(childComplexity), true + + case "JobCreatedActivityLogEntry.environmentName": + if e.ComplexityRoot.JobCreatedActivityLogEntry.EnvironmentName == nil { + break + } + + return e.ComplexityRoot.JobCreatedActivityLogEntry.EnvironmentName(childComplexity), true + + case "JobCreatedActivityLogEntry.id": + if e.ComplexityRoot.JobCreatedActivityLogEntry.ID == nil { + break + } + + return e.ComplexityRoot.JobCreatedActivityLogEntry.ID(childComplexity), true + + case "JobCreatedActivityLogEntry.message": + if e.ComplexityRoot.JobCreatedActivityLogEntry.Message == nil { + break + } + + return e.ComplexityRoot.JobCreatedActivityLogEntry.Message(childComplexity), true + + case "JobCreatedActivityLogEntry.resourceName": + if e.ComplexityRoot.JobCreatedActivityLogEntry.ResourceName == nil { + break + } + + return e.ComplexityRoot.JobCreatedActivityLogEntry.ResourceName(childComplexity), true + + case "JobCreatedActivityLogEntry.resourceType": + if e.ComplexityRoot.JobCreatedActivityLogEntry.ResourceType == nil { + break + } + + return e.ComplexityRoot.JobCreatedActivityLogEntry.ResourceType(childComplexity), true + + case "JobCreatedActivityLogEntry.teamSlug": + if e.ComplexityRoot.JobCreatedActivityLogEntry.TeamSlug == nil { + break + } + + return e.ComplexityRoot.JobCreatedActivityLogEntry.TeamSlug(childComplexity), true + case "JobDeletedActivityLogEntry.actor": if e.ComplexityRoot.JobDeletedActivityLogEntry.Actor == nil { break @@ -6943,6 +7379,69 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.JobTriggeredActivityLogEntry.TeamSlug(childComplexity), true + case "JobUpdatedActivityLogEntry.actor": + if e.ComplexityRoot.JobUpdatedActivityLogEntry.Actor == nil { + break + } + + return e.ComplexityRoot.JobUpdatedActivityLogEntry.Actor(childComplexity), true + + case "JobUpdatedActivityLogEntry.createdAt": + if e.ComplexityRoot.JobUpdatedActivityLogEntry.CreatedAt == nil { + break + } + + return e.ComplexityRoot.JobUpdatedActivityLogEntry.CreatedAt(childComplexity), true + + case "JobUpdatedActivityLogEntry.data": + if e.ComplexityRoot.JobUpdatedActivityLogEntry.Data == nil { + break + } + + return e.ComplexityRoot.JobUpdatedActivityLogEntry.Data(childComplexity), true + + case "JobUpdatedActivityLogEntry.environmentName": + if e.ComplexityRoot.JobUpdatedActivityLogEntry.EnvironmentName == nil { + break + } + + return e.ComplexityRoot.JobUpdatedActivityLogEntry.EnvironmentName(childComplexity), true + + case "JobUpdatedActivityLogEntry.id": + if e.ComplexityRoot.JobUpdatedActivityLogEntry.ID == nil { + break + } + + return e.ComplexityRoot.JobUpdatedActivityLogEntry.ID(childComplexity), true + + case "JobUpdatedActivityLogEntry.message": + if e.ComplexityRoot.JobUpdatedActivityLogEntry.Message == nil { + break + } + + return e.ComplexityRoot.JobUpdatedActivityLogEntry.Message(childComplexity), true + + case "JobUpdatedActivityLogEntry.resourceName": + if e.ComplexityRoot.JobUpdatedActivityLogEntry.ResourceName == nil { + break + } + + return e.ComplexityRoot.JobUpdatedActivityLogEntry.ResourceName(childComplexity), true + + case "JobUpdatedActivityLogEntry.resourceType": + if e.ComplexityRoot.JobUpdatedActivityLogEntry.ResourceType == nil { + break + } + + return e.ComplexityRoot.JobUpdatedActivityLogEntry.ResourceType(childComplexity), true + + case "JobUpdatedActivityLogEntry.teamSlug": + if e.ComplexityRoot.JobUpdatedActivityLogEntry.TeamSlug == nil { + break + } + + return e.ComplexityRoot.JobUpdatedActivityLogEntry.TeamSlug(childComplexity), true + case "KafkaCredentials.accessCert": if e.ComplexityRoot.KafkaCredentials.AccessCert == nil { break @@ -10128,6 +10627,27 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.RequestTeamDeletionPayload.Key(childComplexity), true + case "ResourceChangedField.field": + if e.ComplexityRoot.ResourceChangedField.Field == nil { + break + } + + return e.ComplexityRoot.ResourceChangedField.Field(childComplexity), true + + case "ResourceChangedField.newValue": + if e.ComplexityRoot.ResourceChangedField.NewValue == nil { + break + } + + return e.ComplexityRoot.ResourceChangedField.NewValue(childComplexity), true + + case "ResourceChangedField.oldValue": + if e.ComplexityRoot.ResourceChangedField.OldValue == nil { + break + } + + return e.ComplexityRoot.ResourceChangedField.OldValue(childComplexity), true + case "RestartApplicationPayload.application": if e.ComplexityRoot.RestartApplicationPayload.Application == nil { break @@ -17965,6 +18485,64 @@ type ApplicationScaledActivityLogEntryData { direction: ScalingDirection! } +type ApplicationCreatedActivityLogEntry implements ActivityLogEntry & Node { + "ID of the entry." + id: ID! + + "The identity of the actor who performed the action. The value is either the name of a service account, or the email address of a user." + actor: String! + + "Creation time of the entry." + createdAt: Time! + + "Message that summarizes the entry." + message: String! + + "Type of the resource that was affected by the action." + resourceType: ActivityLogEntryResourceType! + + "Name of the resource that was affected by the action." + resourceName: String! + + "The team slug that the entry belongs to." + teamSlug: Slug! + + "The environment name that the entry belongs to." + environmentName: String + + "Data associated with the creation." + data: GenericKubernetesResourceActivityLogEntryData! +} + +type ApplicationUpdatedActivityLogEntry implements ActivityLogEntry & Node { + "ID of the entry." + id: ID! + + "The identity of the actor who performed the action. The value is either the name of a service account, or the email address of a user." + actor: String! + + "Creation time of the entry." + createdAt: Time! + + "Message that summarizes the entry." + message: String! + + "Type of the resource that was affected by the action." + resourceType: ActivityLogEntryResourceType! + + "Name of the resource that was affected by the action." + resourceName: String! + + "The team slug that the entry belongs to." + teamSlug: Slug! + + "The environment name that the entry belongs to." + environmentName: String + + "Data associated with the update." + data: GenericKubernetesResourceActivityLogEntryData! +} + extend enum ActivityLogActivityType { """ An application was deleted. @@ -17981,6 +18559,99 @@ extend enum ActivityLogActivityType { """ APPLICATION_SCALED } +`, BuiltIn: false}, + {Name: "../schema/apply.graphqls", Input: `extend enum ActivityLogActivityType { + "A generic kubernetes resource was updated via apply." + GENERIC_KUBERNETES_RESOURCE_UPDATED + + "A generic kubernetes resource was created via apply." + GENERIC_KUBERNETES_RESOURCE_CREATED +} + +""" +Additional data associated with a resource created or updated via apply. +""" +type GenericKubernetesResourceActivityLogEntryData { + "The apiVersion of the applied resource." + apiVersion: String! + "The kind of the applied resource." + kind: String! + "The fields that changed during the apply. Only populated for updates." + changedFields: [ResourceChangedField!]! + "GitHub Actions OIDC token claims at the time of the apply. Only present when the request was authenticated via a GitHub token." + gitHubClaims: GitHubActorClaims +} + +""" +GitHub Actions OIDC token claims captured at the time of an apply operation. +""" +type GitHubActorClaims { + "The git ref that triggered the workflow, e.g. 'refs/heads/main'." + ref: String! + "The repository name that triggered the workflow, e.g. 'org/repo'." + repository: String! + "The immutable numeric GitHub repository ID." + repositoryID: String! + "The unique identifier of the Actions workflow run. Links to https://github.com//actions/runs/." + runID: String! + "The attempt number of the workflow run (1-indexed)." + runAttempt: String! + "The GitHub username that triggered the workflow." + actor: String! + "The path to the workflow file, e.g. '.github/workflows/deploy.yaml'." + workflow: String! + "The event that triggered the workflow, e.g. 'push' or 'workflow_dispatch'." + eventName: String! + "The GitHub deployment environment name, if the job targets one." + environment: String! + "The ref of the reusable workflow called by this job, if any. E.g. 'org/repo/.github/workflows/deploy.yaml@refs/heads/main'." + jobWorkflowRef: String! +} + +""" +A single field that changed. +""" +type ResourceChangedField { + "The dot-separated path to the changed field, e.g. 'spec.replicas'." + field: String! + "The value before the change. Null if the field was added." + oldValue: String + "The value after the change. Null if the field was removed." + newValue: String +} + +""" +Activity log entry for a resource kind that is not modelled in the GraphQL API. +The resource type will be the uppercase Kubernetes kind, e.g. 'NAISJOB'. +""" +type GenericKubernetesResourceActivityLogEntry implements ActivityLogEntry & Node { + "ID of the entry." + id: ID! + + "The identity of the actor who performed the action. The value is either the name of a service account, or the email address of a user." + actor: String! + + "Creation time of the entry." + createdAt: Time! + + "Message that summarizes the entry." + message: String! + + "Type of the resource that was affected by the action." + resourceType: ActivityLogEntryResourceType! + + "Name of the resource that was affected by the action." + resourceName: String! + + "The team slug that the entry belongs to." + teamSlug: Slug + + "The environment name that the entry belongs to." + environmentName: String + + "Data associated with the apply operation." + data: GenericKubernetesResourceActivityLogEntryData! +} `, BuiltIn: false}, {Name: "../schema/authz.graphqls", Input: `type Role implements Node { """ @@ -20469,6 +21140,64 @@ type JobRunDeletedActivityLogEntryData { runName: String! } +type JobCreatedActivityLogEntry implements ActivityLogEntry & Node { + "ID of the entry." + id: ID! + + "The identity of the actor who performed the action. The value is either the name of a service account, or the email address of a user." + actor: String! + + "Creation time of the entry." + createdAt: Time! + + "Message that summarizes the entry." + message: String! + + "Type of the resource that was affected by the action." + resourceType: ActivityLogEntryResourceType! + + "Name of the resource that was affected by the action." + resourceName: String! + + "The team slug that the entry belongs to." + teamSlug: Slug! + + "The environment name that the entry belongs to." + environmentName: String + + "Data associated with the creation." + data: GenericKubernetesResourceActivityLogEntryData! +} + +type JobUpdatedActivityLogEntry implements ActivityLogEntry & Node { + "ID of the entry." + id: ID! + + "The identity of the actor who performed the action. The value is either the name of a service account, or the email address of a user." + actor: String! + + "Creation time of the entry." + createdAt: Time! + + "Message that summarizes the entry." + message: String! + + "Type of the resource that was affected by the action." + resourceType: ActivityLogEntryResourceType! + + "Name of the resource that was affected by the action." + resourceName: String! + + "The team slug that the entry belongs to." + teamSlug: Slug! + + "The environment name that the entry belongs to." + environmentName: String + + "Data associated with the update." + data: GenericKubernetesResourceActivityLogEntryData! +} + extend enum ActivityLogActivityType { "Activity log entries related to job deletion." JOB_DELETED diff --git a/internal/graph/gengql/schema.generated.go b/internal/graph/gengql/schema.generated.go index 8e63390fe..8ffe48dbe 100644 --- a/internal/graph/gengql/schema.generated.go +++ b/internal/graph/gengql/schema.generated.go @@ -5966,6 +5966,13 @@ func (ec *executionContext) _Node(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } return ec._KafkaTopic(ctx, sel, obj) + case job.JobUpdatedActivityLogEntry: + return ec._JobUpdatedActivityLogEntry(ctx, sel, &obj) + case *job.JobUpdatedActivityLogEntry: + if obj == nil { + return graphql.Null + } + return ec._JobUpdatedActivityLogEntry(ctx, sel, obj) case job.JobTriggeredActivityLogEntry: return ec._JobTriggeredActivityLogEntry(ctx, sel, &obj) case *job.JobTriggeredActivityLogEntry: @@ -5987,6 +5994,13 @@ func (ec *executionContext) _Node(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } return ec._JobDeletedActivityLogEntry(ctx, sel, obj) + case job.JobCreatedActivityLogEntry: + return ec._JobCreatedActivityLogEntry(ctx, sel, &obj) + case *job.JobCreatedActivityLogEntry: + if obj == nil { + return graphql.Null + } + return ec._JobCreatedActivityLogEntry(ctx, sel, obj) case issue.InvalidSpecIssue: return ec._InvalidSpecIssue(ctx, sel, &obj) case *issue.InvalidSpecIssue: @@ -5994,6 +6008,13 @@ func (ec *executionContext) _Node(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } return ec._InvalidSpecIssue(ctx, sel, obj) + case activitylog1.GenericKubernetesResourceActivityLogEntry: + return ec._GenericKubernetesResourceActivityLogEntry(ctx, sel, &obj) + case *activitylog1.GenericKubernetesResourceActivityLogEntry: + if obj == nil { + return graphql.Null + } + return ec._GenericKubernetesResourceActivityLogEntry(ctx, sel, obj) case issue.FailedSynchronizationIssue: return ec._FailedSynchronizationIssue(ctx, sel, &obj) case *issue.FailedSynchronizationIssue: @@ -6092,6 +6113,13 @@ func (ec *executionContext) _Node(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } return ec._BigQueryDataset(ctx, sel, obj) + case application.ApplicationUpdatedActivityLogEntry: + return ec._ApplicationUpdatedActivityLogEntry(ctx, sel, &obj) + case *application.ApplicationUpdatedActivityLogEntry: + if obj == nil { + return graphql.Null + } + return ec._ApplicationUpdatedActivityLogEntry(ctx, sel, obj) case application.ApplicationScaledActivityLogEntry: return ec._ApplicationScaledActivityLogEntry(ctx, sel, &obj) case *application.ApplicationScaledActivityLogEntry: @@ -6113,6 +6141,13 @@ func (ec *executionContext) _Node(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } return ec._ApplicationDeletedActivityLogEntry(ctx, sel, obj) + case application.ApplicationCreatedActivityLogEntry: + return ec._ApplicationCreatedActivityLogEntry(ctx, sel, &obj) + case *application.ApplicationCreatedActivityLogEntry: + if obj == nil { + return graphql.Null + } + return ec._ApplicationCreatedActivityLogEntry(ctx, sel, obj) case vulnerability.WorkloadVulnerabilitySummary: return ec._WorkloadVulnerabilitySummary(ctx, sel, &obj) case *vulnerability.WorkloadVulnerabilitySummary: diff --git a/internal/graph/schema/applications.graphqls b/internal/graph/schema/applications.graphqls index e11ca7c28..7938830df 100644 --- a/internal/graph/schema/applications.graphqls +++ b/internal/graph/schema/applications.graphqls @@ -717,6 +717,64 @@ type ApplicationScaledActivityLogEntryData { direction: ScalingDirection! } +type ApplicationCreatedActivityLogEntry implements ActivityLogEntry & Node { + "ID of the entry." + id: ID! + + "The identity of the actor who performed the action. The value is either the name of a service account, or the email address of a user." + actor: String! + + "Creation time of the entry." + createdAt: Time! + + "Message that summarizes the entry." + message: String! + + "Type of the resource that was affected by the action." + resourceType: ActivityLogEntryResourceType! + + "Name of the resource that was affected by the action." + resourceName: String! + + "The team slug that the entry belongs to." + teamSlug: Slug! + + "The environment name that the entry belongs to." + environmentName: String + + "Data associated with the creation." + data: GenericKubernetesResourceActivityLogEntryData! +} + +type ApplicationUpdatedActivityLogEntry implements ActivityLogEntry & Node { + "ID of the entry." + id: ID! + + "The identity of the actor who performed the action. The value is either the name of a service account, or the email address of a user." + actor: String! + + "Creation time of the entry." + createdAt: Time! + + "Message that summarizes the entry." + message: String! + + "Type of the resource that was affected by the action." + resourceType: ActivityLogEntryResourceType! + + "Name of the resource that was affected by the action." + resourceName: String! + + "The team slug that the entry belongs to." + teamSlug: Slug! + + "The environment name that the entry belongs to." + environmentName: String + + "Data associated with the update." + data: GenericKubernetesResourceActivityLogEntryData! +} + extend enum ActivityLogActivityType { """ An application was deleted. diff --git a/internal/graph/schema/apply.graphqls b/internal/graph/schema/apply.graphqls new file mode 100644 index 000000000..44a38b20b --- /dev/null +++ b/internal/graph/schema/apply.graphqls @@ -0,0 +1,92 @@ +extend enum ActivityLogActivityType { + "A generic kubernetes resource was updated via apply." + GENERIC_KUBERNETES_RESOURCE_UPDATED + + "A generic kubernetes resource was created via apply." + GENERIC_KUBERNETES_RESOURCE_CREATED +} + +""" +Additional data associated with a resource created or updated via apply. +""" +type GenericKubernetesResourceActivityLogEntryData { + "The apiVersion of the applied resource." + apiVersion: String! + "The kind of the applied resource." + kind: String! + "The fields that changed during the apply. Only populated for updates." + changedFields: [ResourceChangedField!]! + "GitHub Actions OIDC token claims at the time of the apply. Only present when the request was authenticated via a GitHub token." + gitHubClaims: GitHubActorClaims +} + +""" +GitHub Actions OIDC token claims captured at the time of an apply operation. +""" +type GitHubActorClaims { + "The git ref that triggered the workflow, e.g. 'refs/heads/main'." + ref: String! + "The repository name that triggered the workflow, e.g. 'org/repo'." + repository: String! + "The immutable numeric GitHub repository ID." + repositoryID: String! + "The unique identifier of the Actions workflow run. Links to https://github.com//actions/runs/." + runID: String! + "The attempt number of the workflow run (1-indexed)." + runAttempt: String! + "The GitHub username that triggered the workflow." + actor: String! + "The path to the workflow file, e.g. '.github/workflows/deploy.yaml'." + workflow: String! + "The event that triggered the workflow, e.g. 'push' or 'workflow_dispatch'." + eventName: String! + "The GitHub deployment environment name, if the job targets one." + environment: String! + "The ref of the reusable workflow called by this job, if any. E.g. 'org/repo/.github/workflows/deploy.yaml@refs/heads/main'." + jobWorkflowRef: String! +} + +""" +A single field that changed. +""" +type ResourceChangedField { + "The dot-separated path to the changed field, e.g. 'spec.replicas'." + field: String! + "The value before the change. Null if the field was added." + oldValue: String + "The value after the change. Null if the field was removed." + newValue: String +} + +""" +Activity log entry for a resource kind that is not modelled in the GraphQL API. +The resource type will be the uppercase Kubernetes kind, e.g. 'NAISJOB'. +""" +type GenericKubernetesResourceActivityLogEntry implements ActivityLogEntry & Node { + "ID of the entry." + id: ID! + + "The identity of the actor who performed the action. The value is either the name of a service account, or the email address of a user." + actor: String! + + "Creation time of the entry." + createdAt: Time! + + "Message that summarizes the entry." + message: String! + + "Type of the resource that was affected by the action." + resourceType: ActivityLogEntryResourceType! + + "Name of the resource that was affected by the action." + resourceName: String! + + "The team slug that the entry belongs to." + teamSlug: Slug + + "The environment name that the entry belongs to." + environmentName: String + + "Data associated with the apply operation." + data: GenericKubernetesResourceActivityLogEntryData! +} diff --git a/internal/graph/schema/jobs.graphqls b/internal/graph/schema/jobs.graphqls index 519201c13..b177ea53a 100644 --- a/internal/graph/schema/jobs.graphqls +++ b/internal/graph/schema/jobs.graphqls @@ -512,6 +512,64 @@ type JobRunDeletedActivityLogEntryData { runName: String! } +type JobCreatedActivityLogEntry implements ActivityLogEntry & Node { + "ID of the entry." + id: ID! + + "The identity of the actor who performed the action. The value is either the name of a service account, or the email address of a user." + actor: String! + + "Creation time of the entry." + createdAt: Time! + + "Message that summarizes the entry." + message: String! + + "Type of the resource that was affected by the action." + resourceType: ActivityLogEntryResourceType! + + "Name of the resource that was affected by the action." + resourceName: String! + + "The team slug that the entry belongs to." + teamSlug: Slug! + + "The environment name that the entry belongs to." + environmentName: String + + "Data associated with the creation." + data: GenericKubernetesResourceActivityLogEntryData! +} + +type JobUpdatedActivityLogEntry implements ActivityLogEntry & Node { + "ID of the entry." + id: ID! + + "The identity of the actor who performed the action. The value is either the name of a service account, or the email address of a user." + actor: String! + + "Creation time of the entry." + createdAt: Time! + + "Message that summarizes the entry." + message: String! + + "Type of the resource that was affected by the action." + resourceType: ActivityLogEntryResourceType! + + "Name of the resource that was affected by the action." + resourceName: String! + + "The team slug that the entry belongs to." + teamSlug: Slug! + + "The environment name that the entry belongs to." + environmentName: String + + "Data associated with the update." + data: GenericKubernetesResourceActivityLogEntryData! +} + extend enum ActivityLogActivityType { "Activity log entries related to job deletion." JOB_DELETED diff --git a/internal/graph/sse_transport.go b/internal/graph/sse_transport.go index 5e75f9a75..689e34893 100644 --- a/internal/graph/sse_transport.go +++ b/internal/graph/sse_transport.go @@ -205,7 +205,7 @@ func writeJson(w io.Writer, response *graphql.Response) { if err != nil { panic(fmt.Errorf("unable to marshal %s: %w", string(response.Data), err)) } - w.Write(b) + _, _ = w.Write(b) } func jsonDecode(r io.Reader, val any) error { diff --git a/internal/integration/manager.go b/internal/integration/manager.go index 5d1a06572..55101fa00 100644 --- a/internal/integration/manager.go +++ b/internal/integration/manager.go @@ -30,6 +30,7 @@ import ( "github.com/nais/api/internal/persistence/sqlinstance" "github.com/nais/api/internal/rest" "github.com/nais/api/internal/servicemaintenance" + "github.com/nais/api/internal/slug" "github.com/nais/api/internal/thirdparty/aiven" fakeHookd "github.com/nais/api/internal/thirdparty/hookd/fake" "github.com/nais/api/internal/unleash" @@ -43,6 +44,7 @@ import ( "github.com/testcontainers/testcontainers-go/modules/postgres" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/reflect/protoreflect" + "k8s.io/client-go/dynamic" ) type ctxKey int @@ -149,13 +151,13 @@ func newManager(_ context.Context, container *postgres.PostgresContainer, connSt return ctx, nil, nil, err } - gqlRunner, gqlCleanup, err := newGQLRunner(ctx, config, pool, topic, watchers, watcherMgr, clusterConfig, fakeAivenClient, lokiClient) + gqlRunner, gqlCleanup, contextDependencies, err := newGQLRunner(ctx, config, pool, topic, watchers, watcherMgr, clusterConfig, fakeAivenClient, lokiClient) if err != nil { done() return ctx, nil, nil, err } - restRunner, err := newRestRunner(ctx, pool, log) + restRunner, err := newRestRunner(ctx, pool, clusterConfig, k8sRunner, contextDependencies, log) if err != nil { done() return ctx, nil, nil, err @@ -205,8 +207,19 @@ func newManager(_ context.Context, container *postgres.PostgresContainer, connSt } } -func newRestRunner(ctx context.Context, pool *pgxpool.Pool, logger logrus.FieldLogger) (spec.Runner, error) { - router := rest.MakeRouter(ctx, pool, logger) +const testPreSharedKey = "test-pre-shared-key" + +func newRestRunner(ctx context.Context, pool *pgxpool.Pool, clusterConfig kubernetes.ClusterConfigMap, k8sRunner *apiRunner.K8s, contextDependencies func(http.Handler) http.Handler, logger logrus.FieldLogger) (spec.Runner, error) { + router := rest.MakeRouter(ctx, rest.Config{ + Pool: pool, + PreSharedKey: testPreSharedKey, + ContextMiddleware: contextDependencies, + DynamicClient: func(cluster string, _ slug.Slug) (dynamic.Interface, error) { + return k8sRunner.DynamicClient(cluster) + }, + Fakes: rest.Fakes{WithInsecureUserHeader: true}, + Log: logger, + }) return runner.NewRestRunner(router), nil } @@ -221,25 +234,25 @@ func newGQLRunner( clusterConfig kubernetes.ClusterConfigMap, fakeAivenClient *aiven.FakeAivenClient, lokiClient loki.Client, -) (spec.Runner, func(), error) { +) (spec.Runner, func(), func(http.Handler) http.Handler, error) { log := logrus.New() log.Out = io.Discard smMgr, err := servicemaintenance.NewManager(ctx, fakeAivenClient, log.WithField("subsystem", "service_maintenance")) if err != nil { - return nil, nil, err + return nil, nil, nil, err } vMgr, err := vulnerability.NewFakeManager(ctx, log.WithField("subsystem", "vulnerability")) if err != nil { - return nil, nil, err + return nil, nil, nil, err } notifierCtx, notifyCancel := context.WithCancel(ctx) notifier := notify.New(pool, log, notify.WithRetries(0)) go notifier.Run(notifierCtx) - graphMiddleware, err := api.ConfigureGraph( + contextDependencies, err := api.ConfigureGraph( ctx, api.Fakes{ WithFakeKubernetes: true, @@ -280,7 +293,7 @@ func newGQLRunner( ) if err != nil { notifyCancel() - return nil, nil, fmt.Errorf("failed to configure graph: %w", err) + return nil, nil, nil, fmt.Errorf("failed to configure graph: %w", err) } resolver := graph.NewResolver(topic) @@ -290,7 +303,7 @@ func newGQLRunner( Resolvers: resolver, }, hlog) if err != nil { - panic(fmt.Sprintf("failed to create graph handler: %s", err)) + return nil, nil, nil, fmt.Errorf("failed to create graph handler: %w", err) } authProxy := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -318,7 +331,7 @@ func newGQLRunner( middleware.ApiKeyAuthentication()(middleware.RequireAuthenticatedUser()(srv)).ServeHTTP(w, r) }) - return runner.NewGQLRunner(graphMiddleware(authProxy)), notifyCancel, nil + return runner.NewGQLRunner(contextDependencies(authProxy)), notifyCancel, contextDependencies, nil } func startPostgresql(ctx context.Context) (*postgres.PostgresContainer, string, error) { diff --git a/internal/integration/runner/k8s.go b/internal/integration/runner/k8s.go index 153c28e59..3f66b445e 100644 --- a/internal/integration/runner/k8s.go +++ b/internal/integration/runner/k8s.go @@ -89,6 +89,20 @@ func (k *K8s) ClientCreator(cluster string) (dynamic.Interface, watcher.KindReso return c, nil, nil, nil } +// DynamicClient returns the fake dynamic client for the given cluster. +// This is used by the apply handler's DynamicClientFactory in integration tests. +func (k *K8s) DynamicClient(cluster string) (dynamic.Interface, error) { + k.lock.Lock() + defer k.lock.Unlock() + + c, ok := k.clients[cluster] + if !ok { + return nil, fmt.Errorf("cluster %q not found", cluster) + } + + return c, nil +} + func (k *K8s) check(L *lua.LState) int { apiVersion := L.CheckString(1) kind := L.CheckString(2) diff --git a/internal/kubernetes/config.go b/internal/kubernetes/config.go index 0a61633eb..0c34c0995 100644 --- a/internal/kubernetes/config.go +++ b/internal/kubernetes/config.go @@ -6,7 +6,9 @@ import ( "net/http" "strings" + "github.com/nais/api/internal/slug" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" + "k8s.io/client-go/dynamic" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd/api" ) @@ -89,3 +91,17 @@ func (c *StaticCluster) EnvDecode(value string) error { } return nil } + +func (c ClusterConfigMap) TeamClient(environmentName string, teamSlug slug.Slug) (dynamic.Interface, error) { + cfg, ok := c[environmentName] + if !ok { + return nil, fmt.Errorf("unknown environment: %q", environmentName) + } + + impersonatedCfg := rest.CopyConfig(cfg) + impersonatedCfg.Impersonate = rest.ImpersonationConfig{ + UserName: fmt.Sprintf("system:serviceaccount:%[1]v:serviceuser-%[1]v", teamSlug), + } + + return dynamic.NewForConfig(impersonatedCfg) +} diff --git a/internal/kubernetes/fake/fake.go b/internal/kubernetes/fake/fake.go index 98d250028..ab0c7b6bb 100644 --- a/internal/kubernetes/fake/fake.go +++ b/internal/kubernetes/fake/fake.go @@ -287,6 +287,72 @@ func NewDynamicClient(scheme *runtime.Scheme) *dynfake.FakeDynamicClient { return true, modified, nil }) + // Add reactor for server-side apply (ApplyPatchType) support. + // This simulates SSA by treating the patch body as the full desired state, + // creating the object if it doesn't exist or replacing it if it does. + client.PrependReactor("patch", "*", func(action k8stesting.Action) (handled bool, ret runtime.Object, err error) { + patchAction, ok := action.(k8stesting.PatchAction) + if !ok { + return false, nil, nil + } + + if patchAction.GetPatchType() != types.ApplyPatchType { + return false, nil, nil + } + + gvr := patchAction.GetResource() + ns := patchAction.GetNamespace() + name := patchAction.GetName() + + // Parse the patch body as the desired object state. + desired := &unstructured.Unstructured{} + if err := json.Unmarshal(patchAction.GetPatch(), &desired.Object); err != nil { + return true, nil, fmt.Errorf("unmarshaling apply patch: %w", err) + } + + // Try to get the existing object. + existing, err := client.Tracker().Get(gvr, ns, name) + if err != nil { + // Object doesn't exist — create it. + if err := client.Tracker().Create(gvr, desired, ns); err != nil { + return true, nil, fmt.Errorf("creating object via apply: %w", err) + } + return true, desired, nil + } + + // Object exists — merge: start from existing, overlay with desired fields. + existingUnstr, ok := existing.(*unstructured.Unstructured) + if !ok { + return true, nil, fmt.Errorf("expected *unstructured.Unstructured, got %T", existing) + } + + merged := existingUnstr.DeepCopy() + // Overlay all top-level keys from desired onto merged. + for k, v := range desired.Object { + if k == "metadata" { + // Merge metadata rather than replacing it wholesale. + desiredMeta, ok1 := v.(map[string]interface{}) + existingMeta, ok2 := merged.Object["metadata"].(map[string]interface{}) + if ok1 && ok2 { + for mk, mv := range desiredMeta { + existingMeta[mk] = mv + } + merged.Object["metadata"] = existingMeta + } else { + merged.Object[k] = v + } + } else { + merged.Object[k] = v + } + } + + if err := client.Tracker().Update(gvr, merged, ns); err != nil { + return true, nil, fmt.Errorf("updating object via apply: %w", err) + } + + return true, merged, nil + }) + return client } diff --git a/internal/rest/rest.go b/internal/rest/rest.go index 23d852947..3f75a558e 100644 --- a/internal/rest/rest.go +++ b/internal/rest/rest.go @@ -8,17 +8,42 @@ import ( "github.com/go-chi/chi/v5" "github.com/jackc/pgx/v5/pgxpool" + "github.com/nais/api/internal/apply" + "github.com/nais/api/internal/auth/authn" "github.com/nais/api/internal/auth/middleware" "github.com/nais/api/internal/rest/restteamsapi" "github.com/sirupsen/logrus" "golang.org/x/sync/errgroup" ) -func Run(ctx context.Context, listenAddress string, pool *pgxpool.Pool, preSharedKey string, log logrus.FieldLogger) error { - router := MakeRouter(ctx, pool, log, middleware.PreSharedKeyAuthentication(preSharedKey)) +// Fakes contains feature flags for local development and testing. +type Fakes struct { + WithInsecureUserHeader bool +} + +// Config holds all dependencies needed by the REST server. +type Config struct { + ListenAddress string + Pool *pgxpool.Pool + PreSharedKey string + DynamicClient apply.DynamicClientFactory + // ContextMiddleware sets up the request context with all loaders and + // dependencies needed by the apply handler (authz, activitylog, etc.). + // In production this is the middleware returned by ConfigureGraph. + // In tests a minimal equivalent can be provided. + ContextMiddleware func(http.Handler) http.Handler + JWTMiddleware func(http.Handler) http.Handler + GitHubOIDCMiddleware func(http.Handler) http.Handler + AuthHandler authn.Handler + Fakes Fakes + Log logrus.FieldLogger +} + +func Run(ctx context.Context, cfg Config) error { + router := MakeRouter(ctx, cfg) srv := &http.Server{ - Addr: listenAddress, + Addr: cfg.ListenAddress, Handler: router, ReadHeaderTimeout: 5 * time.Second, } @@ -28,32 +53,69 @@ func Run(ctx context.Context, listenAddress string, pool *pgxpool.Pool, preShare <-ctx.Done() ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() - log.Infof("REST HTTP server shutting down...") + cfg.Log.Infof("REST HTTP server shutting down...") if err := srv.Shutdown(ctx); err != nil && !errors.Is(err, context.Canceled) { - log.WithError(err).Infof("HTTP server shutdown failed") + cfg.Log.WithError(err).Infof("HTTP server shutdown failed") return err } return nil }) wg.Go(func() error { - log.Infof("REST HTTP server accepting requests on %q", listenAddress) + cfg.Log.Infof("REST HTTP server accepting requests on %q", cfg.ListenAddress) if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { - log.WithError(err).Infof("unexpected error from HTTP server") + cfg.Log.WithError(err).Infof("unexpected error from HTTP server") return err } - log.Infof("REST HTTP server finished, terminating...") + cfg.Log.Infof("REST HTTP server finished, terminating...") return nil }) return wg.Wait() } -func MakeRouter(ctx context.Context, pool *pgxpool.Pool, log logrus.FieldLogger, middlewares ...func(http.Handler) http.Handler) *chi.Mux { +func MakeRouter(ctx context.Context, cfg Config) *chi.Mux { router := chi.NewRouter() - router.Use(middlewares...) + // Existing pre-shared-key authenticated routes. + if cfg.PreSharedKey != "" { + router.Group(func(r chi.Router) { + r.Use(middleware.PreSharedKeyAuthentication(cfg.PreSharedKey)) + r.Get("/teams/{teamSlug}", restteamsapi.TeamsApiHandler(ctx, cfg.Pool, cfg.Log)) + }) + } + + // Apply route with user authentication. + router.Group(func(r chi.Router) { + r.Use(cfg.ContextMiddleware) + + if cfg.Fakes.WithInsecureUserHeader { + cfg.Log.Info("#### Middleware enabled: InsecureUserHeader (for testing only) ####") + r.Use(middleware.InsecureUserHeader()) + } + + if cfg.JWTMiddleware != nil { + cfg.Log.Info("#### Middleware enabled: JWT Authentication ####") + r.Use(cfg.JWTMiddleware) + } + + if cfg.AuthHandler != nil { + cfg.Log.Info("#### Middleware enabled: OAuth2 Authentication ####") + r.Use(middleware.Oauth2Authentication(cfg.AuthHandler)) + } + + if cfg.GitHubOIDCMiddleware != nil { + cfg.Log.Info("#### Middleware enabled: GitHub OIDC Authentication ####") + r.Use(cfg.GitHubOIDCMiddleware) + } + + r.Use( + middleware.ApiKeyAuthentication(), + middleware.RequireAuthenticatedUser(), + ) - router.Get("/teams/{teamSlug}", restteamsapi.TeamsApiHandler(ctx, pool, log)) + handler := apply.NewHandler(cfg.DynamicClient, cfg.Log) + r.Post("/api/v1/teams/{teamSlug}/environments/{environment}/apply", handler.ServeHTTP) + }) return router } diff --git a/internal/workload/application/activitylog.go b/internal/workload/application/activitylog.go index 40d19fb04..3e18da11f 100644 --- a/internal/workload/application/activitylog.go +++ b/internal/workload/application/activitylog.go @@ -8,14 +8,15 @@ import ( ) const ( - activityLogEntryResourceTypeApplication activitylog.ActivityLogEntryResourceType = "APP" + ActivityLogEntryResourceTypeApplication activitylog.ActivityLogEntryResourceType = "APP" activityLogEntryActionRestartApplication activitylog.ActivityLogEntryAction = "RESTARTED" activityLogEntryActionAutoScaleApplication activitylog.ActivityLogEntryAction = "AUTOSCALE" ) func init() { - activitylog.RegisterTransformer(activityLogEntryResourceTypeApplication, func(entry activitylog.GenericActivityLogEntry) (activitylog.ActivityLogEntry, error) { + activitylog.RegisterKindResourceType("Application", ActivityLogEntryResourceTypeApplication) + activitylog.RegisterTransformer(ActivityLogEntryResourceTypeApplication, func(entry activitylog.GenericActivityLogEntry) (activitylog.ActivityLogEntry, error) { switch entry.Action { case activityLogEntryActionRestartApplication: if entry.TeamSlug == nil { @@ -55,15 +56,35 @@ func init() { GenericActivityLogEntry: entry.WithMessage("Application deployed"), Data: data, }, nil + case activitylog.ActivityLogEntryActionCreated: + data, err := activitylog.UnmarshalData[activitylog.GenericKubernetesResourceActivityLogEntryData](entry) + if err != nil { + return nil, fmt.Errorf("transforming application created activity log entry data: %w", err) + } + return ApplicationCreatedActivityLogEntry{ + GenericActivityLogEntry: entry.WithMessage(fmt.Sprintf("Application %s created", entry.ResourceName)), + Data: data, + }, nil + case activitylog.ActivityLogEntryActionUpdated: + data, err := activitylog.UnmarshalData[activitylog.GenericKubernetesResourceActivityLogEntryData](entry) + if err != nil { + return nil, fmt.Errorf("transforming application updated activity log entry data: %w", err) + } + return ApplicationUpdatedActivityLogEntry{ + GenericActivityLogEntry: entry.WithMessage(fmt.Sprintf("Application %s updated", entry.ResourceName)), + Data: data, + }, nil default: return nil, fmt.Errorf("unsupported application activity log entry action: %q", entry.Action) } }) - activitylog.RegisterFilter("APPLICATION_DELETED", activitylog.ActivityLogEntryActionDeleted, activityLogEntryResourceTypeApplication) - activitylog.RegisterFilter("APPLICATION_RESTARTED", activityLogEntryActionRestartApplication, activityLogEntryResourceTypeApplication) - activitylog.RegisterFilter("APPLICATION_SCALED", activityLogEntryActionAutoScaleApplication, activityLogEntryResourceTypeApplication) - activitylog.RegisterFilter("DEPLOYMENT", deploymentactivity.ActivityLogEntryActionDeployment, activityLogEntryResourceTypeApplication) + activitylog.RegisterFilter("APPLICATION_DELETED", activitylog.ActivityLogEntryActionDeleted, ActivityLogEntryResourceTypeApplication) + activitylog.RegisterFilter("APPLICATION_RESTARTED", activityLogEntryActionRestartApplication, ActivityLogEntryResourceTypeApplication) + activitylog.RegisterFilter("APPLICATION_SCALED", activityLogEntryActionAutoScaleApplication, ActivityLogEntryResourceTypeApplication) + activitylog.RegisterFilter("DEPLOYMENT", deploymentactivity.ActivityLogEntryActionDeployment, ActivityLogEntryResourceTypeApplication) + activitylog.RegisterFilter("GENERIC_KUBERNETES_RESOURCE_CREATED", activitylog.ActivityLogEntryActionCreated, ActivityLogEntryResourceTypeApplication) + activitylog.RegisterFilter("GENERIC_KUBERNETES_RESOURCE_UPDATED", activitylog.ActivityLogEntryActionUpdated, ActivityLogEntryResourceTypeApplication) } type ApplicationRestartedActivityLogEntry struct { @@ -84,3 +105,15 @@ type ApplicationScaledActivityLogEntryData struct { NewSize int `json:"newSize,string"` Direction ScalingDirection `json:"direction"` } + +type ApplicationCreatedActivityLogEntry struct { + activitylog.GenericActivityLogEntry + + Data *activitylog.GenericKubernetesResourceActivityLogEntryData `json:"data"` +} + +type ApplicationUpdatedActivityLogEntry struct { + activitylog.GenericActivityLogEntry + + Data *activitylog.GenericKubernetesResourceActivityLogEntryData `json:"data"` +} diff --git a/internal/workload/application/queries.go b/internal/workload/application/queries.go index edf1f3607..2c14c9436 100644 --- a/internal/workload/application/queries.go +++ b/internal/workload/application/queries.go @@ -116,7 +116,7 @@ func Delete(ctx context.Context, teamSlug slug.Slug, environmentName, name strin if err := activitylog.Create(ctx, activitylog.CreateInput{ Action: activitylog.ActivityLogEntryActionDeleted, - ResourceType: activityLogEntryResourceTypeApplication, + ResourceType: ActivityLogEntryResourceTypeApplication, TeamSlug: &teamSlug, EnvironmentName: &environmentName, ResourceName: name, @@ -151,7 +151,7 @@ func Restart(ctx context.Context, teamSlug slug.Slug, environmentName, name stri return activitylog.Create(ctx, activitylog.CreateInput{ Action: activityLogEntryActionRestartApplication, - ResourceType: activityLogEntryResourceTypeApplication, + ResourceType: ActivityLogEntryResourceTypeApplication, TeamSlug: &teamSlug, EnvironmentName: &environmentName, ResourceName: name, diff --git a/internal/workload/job/activitylog.go b/internal/workload/job/activitylog.go index 3476eb1d3..05f94ad9f 100644 --- a/internal/workload/job/activitylog.go +++ b/internal/workload/job/activitylog.go @@ -8,13 +8,14 @@ import ( ) const ( - activityLogEntryResourceTypeJob activitylog.ActivityLogEntryResourceType = "JOB" + ActivityLogEntryResourceTypeJob activitylog.ActivityLogEntryResourceType = "JOB" activityLogEntryActionTriggerJob activitylog.ActivityLogEntryAction = "TRIGGER_JOB" activityLogEntryActionDeleteJobRun activitylog.ActivityLogEntryAction = "DELETE_JOB_RUN" ) func init() { - activitylog.RegisterTransformer(activityLogEntryResourceTypeJob, func(entry activitylog.GenericActivityLogEntry) (activitylog.ActivityLogEntry, error) { + activitylog.RegisterKindResourceType("Naisjob", ActivityLogEntryResourceTypeJob) + activitylog.RegisterTransformer(ActivityLogEntryResourceTypeJob, func(entry activitylog.GenericActivityLogEntry) (activitylog.ActivityLogEntry, error) { switch entry.Action { case activityLogEntryActionTriggerJob: return JobTriggeredActivityLogEntry{ @@ -46,21 +47,41 @@ func init() { case deploymentactivity.ActivityLogEntryActionDeployment: data, err := activitylog.UnmarshalData[deploymentactivity.DeploymentActivityLogEntryData](entry) if err != nil { - return nil, fmt.Errorf("transforming job scaled activity log entry data: %w", err) + return nil, fmt.Errorf("transforming job deployment activity log entry data: %w", err) } return deploymentactivity.DeploymentActivityLogEntry{ GenericActivityLogEntry: entry.WithMessage("Job deployed"), Data: data, }, nil + case activitylog.ActivityLogEntryActionCreated: + data, err := activitylog.UnmarshalData[activitylog.GenericKubernetesResourceActivityLogEntryData](entry) + if err != nil { + return nil, fmt.Errorf("transforming job created activity log entry data: %w", err) + } + return JobCreatedActivityLogEntry{ + GenericActivityLogEntry: entry.WithMessage(fmt.Sprintf("Job %s created", entry.ResourceName)), + Data: data, + }, nil + case activitylog.ActivityLogEntryActionUpdated: + data, err := activitylog.UnmarshalData[activitylog.GenericKubernetesResourceActivityLogEntryData](entry) + if err != nil { + return nil, fmt.Errorf("transforming job updated activity log entry data: %w", err) + } + return JobUpdatedActivityLogEntry{ + GenericActivityLogEntry: entry.WithMessage(fmt.Sprintf("Job %s updated", entry.ResourceName)), + Data: data, + }, nil default: return nil, fmt.Errorf("unsupported job activity log entry action: %q", entry.Action) } }) - activitylog.RegisterFilter("JOB_DELETED", activitylog.ActivityLogEntryActionDeleted, activityLogEntryResourceTypeJob) - activitylog.RegisterFilter("JOB_RUN_DELETED", activityLogEntryActionDeleteJobRun, activityLogEntryResourceTypeJob) - activitylog.RegisterFilter("JOB_TRIGGERED", activityLogEntryActionTriggerJob, activityLogEntryResourceTypeJob) - activitylog.RegisterFilter("DEPLOYMENT", deploymentactivity.ActivityLogEntryActionDeployment, activityLogEntryResourceTypeJob) + activitylog.RegisterFilter("JOB_DELETED", activitylog.ActivityLogEntryActionDeleted, ActivityLogEntryResourceTypeJob) + activitylog.RegisterFilter("JOB_RUN_DELETED", activityLogEntryActionDeleteJobRun, ActivityLogEntryResourceTypeJob) + activitylog.RegisterFilter("JOB_TRIGGERED", activityLogEntryActionTriggerJob, ActivityLogEntryResourceTypeJob) + activitylog.RegisterFilter("DEPLOYMENT", deploymentactivity.ActivityLogEntryActionDeployment, ActivityLogEntryResourceTypeJob) + activitylog.RegisterFilter("GENERIC_KUBERNETES_RESOURCE_CREATED", activitylog.ActivityLogEntryActionCreated, ActivityLogEntryResourceTypeJob) + activitylog.RegisterFilter("GENERIC_KUBERNETES_RESOURCE_UPDATED", activitylog.ActivityLogEntryActionUpdated, ActivityLogEntryResourceTypeJob) } type JobTriggeredActivityLogEntry struct { @@ -79,3 +100,15 @@ type JobRunDeletedActivityLogEntry struct { activitylog.GenericActivityLogEntry Data *JobRunDeletedActivityLogEntryData } + +type JobCreatedActivityLogEntry struct { + activitylog.GenericActivityLogEntry + + Data *activitylog.GenericKubernetesResourceActivityLogEntryData `json:"data"` +} + +type JobUpdatedActivityLogEntry struct { + activitylog.GenericActivityLogEntry + + Data *activitylog.GenericKubernetesResourceActivityLogEntryData `json:"data"` +} diff --git a/internal/workload/job/queries.go b/internal/workload/job/queries.go index ba83a7acd..160cbe959 100644 --- a/internal/workload/job/queries.go +++ b/internal/workload/job/queries.go @@ -170,7 +170,7 @@ func Delete(ctx context.Context, teamSlug slug.Slug, environmentName, name strin if err := activitylog.Create(ctx, activitylog.CreateInput{ Action: activitylog.ActivityLogEntryActionDeleted, Actor: authz.ActorFromContext(ctx).User, - ResourceType: activityLogEntryResourceTypeJob, + ResourceType: ActivityLogEntryResourceTypeJob, ResourceName: name, EnvironmentName: &environmentName, TeamSlug: &teamSlug, @@ -203,7 +203,7 @@ func DeleteRun(ctx context.Context, teamSlug slug.Slug, environmentName, runName if err := activitylog.Create(ctx, activitylog.CreateInput{ Action: activityLogEntryActionDeleteJobRun, Actor: authz.ActorFromContext(ctx).User, - ResourceType: activityLogEntryResourceTypeJob, + ResourceType: ActivityLogEntryResourceTypeJob, ResourceName: jobName, Data: &JobRunDeletedActivityLogEntryData{RunName: runName}, EnvironmentName: &environmentName, @@ -250,7 +250,7 @@ func Trigger(ctx context.Context, teamSlug slug.Slug, environmentName, name, run if err := activitylog.Create(ctx, activitylog.CreateInput{ Action: activityLogEntryActionTriggerJob, Actor: authz.ActorFromContext(ctx).User, - ResourceType: activityLogEntryResourceTypeJob, + ResourceType: ActivityLogEntryResourceTypeJob, ResourceName: name, EnvironmentName: &environmentName, TeamSlug: &teamSlug, diff --git a/mise.toml b/mise.toml index 3eff67b32..965d4d408 100644 --- a/mise.toml +++ b/mise.toml @@ -8,3 +8,4 @@ node = "24" protoc = "29.3" protoc-gen-go = "1.36.4" protoc-gen-go-grpc = "1.5.1" +"github:CppCXY/EmmyLuaCodeStyle" = "1.5.6"