diff --git a/integration_tests/ingress_metrics.lua b/integration_tests/ingress_metrics.lua deleted file mode 100644 index 916877836..000000000 --- a/integration_tests/ingress_metrics.lua +++ /dev/null @@ -1,85 +0,0 @@ -Helper.readK8sResources("k8s_resources/simple") - -local user = User.new("user-1", "usr@ex.com", "ei") -Team.new("slug-1", "team-name", "#team") - -local function iso8601_with_tz(ts) - local s = os.date("%Y-%m-%dT%H:%M:%S%z", ts) - -- s is a string at runtime, but assert to satisfy the type checker if needed: - assert(type(s) == "string") - return s:sub(1, -3) .. ":" .. s:sub(-2) -end - -local now = os.time() -local one_week_ago = now - 7 * 24 * 60 * 60 - -local from = iso8601_with_tz(one_week_ago) -local to = iso8601_with_tz(now) - - -Test.gql("Ingress metrics", function(t) - t.addHeader("x-user-email", user:email()) - t.query(string.format([[ - query { - team(slug: "slug-1") { - applications { - nodes { - name - ingresses { - metrics { - requestsPerSecond - errorsPerSecond - requestsPerSecondSeries: series(input: {type: REQUESTS_PER_SECOND, start: "%s", end: "%s"}) { - timestamp - value - } - } - - } - } - pageInfo { - totalCount - } - } - } - } - ]], from, to)) - - t.check { - data = { - team = { - applications = { - nodes = { - { - name = "another-app", - ingresses = { - { - metrics = { - requestsPerSecond = NotNull(), - errorsPerSecond = NotNull(), - requestsPerSecondSeries = NotNull(), - }, - }, - }, - }, - { - name = "app-name", - ingresses = { - { - metrics = { - requestsPerSecond = NotNull(), - errorsPerSecond = NotNull(), - requestsPerSecondSeries = NotNull(), - }, - }, - }, - }, - }, - pageInfo = { - totalCount = 2, - }, - }, - }, - }, - } -end) diff --git a/internal/cmd/api/http.go b/internal/cmd/api/http.go index 4ca6f5851..155180265 100644 --- a/internal/cmd/api/http.go +++ b/internal/cmd/api/http.go @@ -338,7 +338,7 @@ func ConfigureGraph( setupContext := func(ctx context.Context) context.Context { ctx = podlog.NewLoaderContext(ctx, podLogStreamer) - ctx = application.NewLoaderContext(ctx, watchers.AppWatcher, watchers.IngressWatcher, prometheusClient, log) + ctx = application.NewLoaderContext(ctx, watchers.AppWatcher, watchers.IngressWatcher, log) ctx = bigquery.NewLoaderContext(ctx, watchers.BqWatcher) ctx = bucket.NewLoaderContext(ctx, watchers.BucketWatcher) ctx = job.NewLoaderContext(ctx, watchers.JobWatcher, watchers.RunWatcher) diff --git a/internal/graph/applications.resolvers.go b/internal/graph/applications.resolvers.go index e4461d19f..682872fb4 100644 --- a/internal/graph/applications.resolvers.go +++ b/internal/graph/applications.resolvers.go @@ -117,24 +117,6 @@ func (r *ingressResolver) Type(ctx context.Context, obj *application.Ingress) (a return application.GetIngressType(ctx, obj), nil } -func (r *ingressResolver) Metrics(ctx context.Context, obj *application.Ingress) (*application.IngressMetrics, error) { - return &application.IngressMetrics{ - Ingress: obj, - }, nil -} - -func (r *ingressMetricsResolver) RequestsPerSecond(ctx context.Context, obj *application.IngressMetrics) (float64, error) { - return application.RequestsPerSecondForIngress(ctx, obj) -} - -func (r *ingressMetricsResolver) ErrorsPerSecond(ctx context.Context, obj *application.IngressMetrics) (float64, error) { - return application.ErrorsPerSecondForIngress(ctx, obj) -} - -func (r *ingressMetricsResolver) Series(ctx context.Context, obj *application.IngressMetrics, input application.IngressMetricsInput) ([]*application.IngressMetricSample, error) { - return application.SeriesForIngress(ctx, obj, input) -} - func (r *mutationResolver) DeleteApplication(ctx context.Context, input application.DeleteApplicationInput) (*application.DeleteApplicationPayload, error) { if err := authz.CanDeleteApplications(ctx, input.TeamSlug); err != nil { return nil, err @@ -225,8 +207,6 @@ func (r *Resolver) DeleteApplicationPayload() gengql.DeleteApplicationPayloadRes func (r *Resolver) Ingress() gengql.IngressResolver { return &ingressResolver{r} } -func (r *Resolver) IngressMetrics() gengql.IngressMetricsResolver { return &ingressMetricsResolver{r} } - func (r *Resolver) RestartApplicationPayload() gengql.RestartApplicationPayloadResolver { return &restartApplicationPayloadResolver{r} } @@ -241,7 +221,6 @@ type ( applicationInstanceResolver struct{ *Resolver } deleteApplicationPayloadResolver struct{ *Resolver } ingressResolver struct{ *Resolver } - ingressMetricsResolver struct{ *Resolver } restartApplicationPayloadResolver struct{ *Resolver } updateApplicationPayloadResolver struct{ *Resolver } ) diff --git a/internal/graph/gengql/applications.generated.go b/internal/graph/gengql/applications.generated.go index d6441ecaa..738da5aa7 100644 --- a/internal/graph/gengql/applications.generated.go +++ b/internal/graph/gengql/applications.generated.go @@ -87,12 +87,6 @@ type DeleteApplicationPayloadResolver interface { } type IngressResolver interface { Type(ctx context.Context, obj *application.Ingress) (application.IngressType, error) - Metrics(ctx context.Context, obj *application.Ingress) (*application.IngressMetrics, error) -} -type IngressMetricsResolver interface { - RequestsPerSecond(ctx context.Context, obj *application.IngressMetrics) (float64, error) - ErrorsPerSecond(ctx context.Context, obj *application.IngressMetrics) (float64, error) - Series(ctx context.Context, obj *application.IngressMetrics, input application.IngressMetricsInput) ([]*application.IngressMetricSample, error) } type RestartApplicationPayloadResolver interface { Application(ctx context.Context, obj *application.RestartApplicationPayload) (*application.Application, error) @@ -483,20 +477,6 @@ func (ec *executionContext) field_Application_vulnerabilityFixHistory_args(ctx c return args, nil } -func (ec *executionContext) field_IngressMetrics_series_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { - var err error - args := map[string]any{} - arg0, err := graphql.ProcessArgField(ctx, rawArgs, "input", - func(ctx context.Context, v any) (application.IngressMetricsInput, error) { - return ec.unmarshalNIngressMetricsInput2githubᚗcomᚋnaisᚋapiᚋinternalᚋworkloadᚋapplicationᚐIngressMetricsInput(ctx, v) - }) - if err != nil { - return nil, err - } - args["input"] = arg0 - return args, nil -} - // endregion ***************************** args.gotpl ***************************** // region ************************** directives.gotpl ************************** @@ -4014,174 +3994,6 @@ func (ec *executionContext) fieldContext_Ingress_type(_ context.Context, field g return graphql.NewScalarFieldContext("Ingress", field, true, true, errors.New("field of type IngressType does not have child fields")) } -func (ec *executionContext) _Ingress_metrics(ctx context.Context, field graphql.CollectedField, obj *application.Ingress) (ret graphql.Marshaler) { - return graphql.ResolveField( - ctx, - ec.OperationContext, - field, - func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Ingress_metrics(ctx, field) - }, - func(ctx context.Context) (any, error) { - return ec.Resolvers.Ingress().Metrics(ctx, obj) - }, - nil, - func(ctx context.Context, selections ast.SelectionSet, v *application.IngressMetrics) graphql.Marshaler { - return ec.marshalNIngressMetrics2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋworkloadᚋapplicationᚐIngressMetrics(ctx, selections, v) - }, - true, - true, - ) -} -func (ec *executionContext) fieldContext_Ingress_metrics(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Ingress", - Field: field, - IsMethod: true, - IsResolver: true, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_IngressMetrics(ctx, field) - }, - } - return fc, nil -} - -func (ec *executionContext) _IngressMetricSample_timestamp(ctx context.Context, field graphql.CollectedField, obj *application.IngressMetricSample) (ret graphql.Marshaler) { - return graphql.ResolveField( - ctx, - ec.OperationContext, - field, - func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_IngressMetricSample_timestamp(ctx, field) - }, - func(ctx context.Context) (any, error) { - return obj.Timestamp, nil - }, - nil, - func(ctx context.Context, selections ast.SelectionSet, v time.Time) graphql.Marshaler { - return ec.marshalNTime2timeᚐTime(ctx, selections, v) - }, - true, - true, - ) -} -func (ec *executionContext) fieldContext_IngressMetricSample_timestamp(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("IngressMetricSample", field, false, false, errors.New("field of type Time does not have child fields")) -} - -func (ec *executionContext) _IngressMetricSample_value(ctx context.Context, field graphql.CollectedField, obj *application.IngressMetricSample) (ret graphql.Marshaler) { - return graphql.ResolveField( - ctx, - ec.OperationContext, - field, - func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_IngressMetricSample_value(ctx, field) - }, - func(ctx context.Context) (any, error) { - return obj.Value, nil - }, - nil, - func(ctx context.Context, selections ast.SelectionSet, v float64) graphql.Marshaler { - return ec.marshalNFloat2float64(ctx, selections, v) - }, - true, - true, - ) -} -func (ec *executionContext) fieldContext_IngressMetricSample_value(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("IngressMetricSample", field, false, false, errors.New("field of type Float does not have child fields")) -} - -func (ec *executionContext) _IngressMetrics_requestsPerSecond(ctx context.Context, field graphql.CollectedField, obj *application.IngressMetrics) (ret graphql.Marshaler) { - return graphql.ResolveField( - ctx, - ec.OperationContext, - field, - func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_IngressMetrics_requestsPerSecond(ctx, field) - }, - func(ctx context.Context) (any, error) { - return ec.Resolvers.IngressMetrics().RequestsPerSecond(ctx, obj) - }, - nil, - func(ctx context.Context, selections ast.SelectionSet, v float64) graphql.Marshaler { - return ec.marshalNFloat2float64(ctx, selections, v) - }, - true, - true, - ) -} -func (ec *executionContext) fieldContext_IngressMetrics_requestsPerSecond(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("IngressMetrics", field, true, true, errors.New("field of type Float does not have child fields")) -} - -func (ec *executionContext) _IngressMetrics_errorsPerSecond(ctx context.Context, field graphql.CollectedField, obj *application.IngressMetrics) (ret graphql.Marshaler) { - return graphql.ResolveField( - ctx, - ec.OperationContext, - field, - func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_IngressMetrics_errorsPerSecond(ctx, field) - }, - func(ctx context.Context) (any, error) { - return ec.Resolvers.IngressMetrics().ErrorsPerSecond(ctx, obj) - }, - nil, - func(ctx context.Context, selections ast.SelectionSet, v float64) graphql.Marshaler { - return ec.marshalNFloat2float64(ctx, selections, v) - }, - true, - true, - ) -} -func (ec *executionContext) fieldContext_IngressMetrics_errorsPerSecond(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("IngressMetrics", field, true, true, errors.New("field of type Float does not have child fields")) -} - -func (ec *executionContext) _IngressMetrics_series(ctx context.Context, field graphql.CollectedField, obj *application.IngressMetrics) (ret graphql.Marshaler) { - return graphql.ResolveField( - ctx, - ec.OperationContext, - field, - func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_IngressMetrics_series(ctx, field) - }, - func(ctx context.Context) (any, error) { - fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.IngressMetrics().Series(ctx, obj, fc.Args["input"].(application.IngressMetricsInput)) - }, - nil, - func(ctx context.Context, selections ast.SelectionSet, v []*application.IngressMetricSample) graphql.Marshaler { - return ec.marshalNIngressMetricSample2ᚕᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋworkloadᚋapplicationᚐIngressMetricSampleᚄ(ctx, selections, v) - }, - true, - true, - ) -} -func (ec *executionContext) fieldContext_IngressMetrics_series(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "IngressMetrics", - Field: field, - IsMethod: true, - IsResolver: true, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_IngressMetricSample(ctx, field) - }, - } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) - } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_IngressMetrics_series_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return fc, err - } - return fc, nil -} - func (ec *executionContext) _KafkaLagScalingStrategy_threshold(ctx context.Context, field graphql.CollectedField, obj *application.KafkaLagScalingStrategy) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -4492,50 +4304,6 @@ func (ec *executionContext) unmarshalInputDeleteApplicationInput(ctx context.Con return it, nil } -func (ec *executionContext) unmarshalInputIngressMetricsInput(ctx context.Context, obj any) (application.IngressMetricsInput, error) { - var it application.IngressMetricsInput - if obj == nil { - return it, nil - } - - asMap := map[string]any{} - for k, v := range obj.(map[string]any) { - asMap[k] = v - } - - fieldsInOrder := [...]string{"start", "end", "type"} - for _, k := range fieldsInOrder { - v, ok := asMap[k] - if !ok { - continue - } - switch k { - case "start": - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("start")) - data, err := ec.unmarshalNTime2timeᚐTime(ctx, v) - if err != nil { - return it, err - } - it.Start = data - case "end": - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("end")) - data, err := ec.unmarshalNTime2timeᚐTime(ctx, v) - if err != nil { - return it, err - } - it.End = data - case "type": - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("type")) - data, err := ec.unmarshalNIngressMetricsType2githubᚗcomᚋnaisᚋapiᚋinternalᚋworkloadᚋapplicationᚐIngressMetricsType(ctx, v) - if err != nil { - return it, err - } - it.Type = data - } - } - return it, nil -} - func (ec *executionContext) unmarshalInputRestartApplicationInput(ctx context.Context, obj any) (application.RestartApplicationInput, error) { var it application.RestartApplicationInput if obj == nil { @@ -7196,228 +6964,6 @@ func (ec *executionContext) _Ingress(ctx context.Context, sel ast.SelectionSet, continue } - out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "metrics": - field := field - - innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._Ingress_metrics(ctx, field, obj) - if res == graphql.Null { - atomic.AddUint32(&fs.Invalids, 1) - } - return res - } - - if field.Deferrable != nil { - dfs, ok := deferred[field.Deferrable.Label] - di := 0 - if ok { - dfs.AddField(field) - di = len(dfs.Values) - 1 - } else { - dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) - deferred[field.Deferrable.Label] = dfs - } - dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { - return innerFunc(ctx, dfs) - }) - - // don't run the out.Concurrently() call below - out.Values[i] = graphql.Null - continue - } - - out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.Deferred, int32(min(len(deferred), math.MaxInt32))) - - for label, dfs := range deferred { - ec.ProcessDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - -var ingressMetricSampleImplementors = []string{"IngressMetricSample"} - -func (ec *executionContext) _IngressMetricSample(ctx context.Context, sel ast.SelectionSet, obj *application.IngressMetricSample) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, ingressMetricSampleImplementors) - - 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("IngressMetricSample") - case "timestamp": - out.Values[i] = ec._IngressMetricSample_timestamp(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "value": - out.Values[i] = ec._IngressMetricSample_value(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(min(len(deferred), math.MaxInt32))) - - for label, dfs := range deferred { - ec.ProcessDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - -var ingressMetricsImplementors = []string{"IngressMetrics"} - -func (ec *executionContext) _IngressMetrics(ctx context.Context, sel ast.SelectionSet, obj *application.IngressMetrics) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, ingressMetricsImplementors) - - 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("IngressMetrics") - case "requestsPerSecond": - field := field - - innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._IngressMetrics_requestsPerSecond(ctx, field, obj) - if res == graphql.Null { - atomic.AddUint32(&fs.Invalids, 1) - } - return res - } - - if field.Deferrable != nil { - dfs, ok := deferred[field.Deferrable.Label] - di := 0 - if ok { - dfs.AddField(field) - di = len(dfs.Values) - 1 - } else { - dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) - deferred[field.Deferrable.Label] = dfs - } - dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { - return innerFunc(ctx, dfs) - }) - - // don't run the out.Concurrently() call below - out.Values[i] = graphql.Null - continue - } - - out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "errorsPerSecond": - field := field - - innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._IngressMetrics_errorsPerSecond(ctx, field, obj) - if res == graphql.Null { - atomic.AddUint32(&fs.Invalids, 1) - } - return res - } - - if field.Deferrable != nil { - dfs, ok := deferred[field.Deferrable.Label] - di := 0 - if ok { - dfs.AddField(field) - di = len(dfs.Values) - 1 - } else { - dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) - deferred[field.Deferrable.Label] = dfs - } - dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { - return innerFunc(ctx, dfs) - }) - - // don't run the out.Concurrently() call below - out.Values[i] = graphql.Null - continue - } - - out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "series": - field := field - - innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._IngressMetrics_series(ctx, field, obj) - if res == graphql.Null { - atomic.AddUint32(&fs.Invalids, 1) - } - return res - } - - if field.Deferrable != nil { - dfs, ok := deferred[field.Deferrable.Label] - di := 0 - if ok { - dfs.AddField(field) - di = len(dfs.Values) - 1 - } else { - dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) - deferred[field.Deferrable.Label] = dfs - } - dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { - return innerFunc(ctx, dfs) - }) - - // don't run the out.Concurrently() call below - out.Values[i] = graphql.Null - continue - } - out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) default: panic("unknown field " + strconv.Quote(field.Name)) @@ -8018,61 +7564,6 @@ func (ec *executionContext) marshalNIngress2ᚖgithubᚗcomᚋnaisᚋapiᚋinter return ec._Ingress(ctx, sel, v) } -func (ec *executionContext) marshalNIngressMetricSample2ᚕᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋworkloadᚋapplicationᚐIngressMetricSampleᚄ(ctx context.Context, sel ast.SelectionSet, v []*application.IngressMetricSample) 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.marshalNIngressMetricSample2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋworkloadᚋapplicationᚐIngressMetricSample(ctx, sel, v[i]) - }) - - for _, e := range ret { - if e == graphql.Null { - return graphql.Null - } - } - - return ret -} - -func (ec *executionContext) marshalNIngressMetricSample2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋworkloadᚋapplicationᚐIngressMetricSample(ctx context.Context, sel ast.SelectionSet, v *application.IngressMetricSample) 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._IngressMetricSample(ctx, sel, v) -} - -func (ec *executionContext) marshalNIngressMetrics2githubᚗcomᚋnaisᚋapiᚋinternalᚋworkloadᚋapplicationᚐIngressMetrics(ctx context.Context, sel ast.SelectionSet, v application.IngressMetrics) graphql.Marshaler { - return ec._IngressMetrics(ctx, sel, &v) -} - -func (ec *executionContext) marshalNIngressMetrics2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋworkloadᚋapplicationᚐIngressMetrics(ctx context.Context, sel ast.SelectionSet, v *application.IngressMetrics) 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._IngressMetrics(ctx, sel, v) -} - -func (ec *executionContext) unmarshalNIngressMetricsInput2githubᚗcomᚋnaisᚋapiᚋinternalᚋworkloadᚋapplicationᚐIngressMetricsInput(ctx context.Context, v any) (application.IngressMetricsInput, error) { - res, err := ec.unmarshalInputIngressMetricsInput(ctx, v) - return res, graphql.ErrorOnPath(ctx, err) -} - -func (ec *executionContext) unmarshalNIngressMetricsType2githubᚗcomᚋnaisᚋapiᚋinternalᚋworkloadᚋapplicationᚐIngressMetricsType(ctx context.Context, v any) (application.IngressMetricsType, error) { - var res application.IngressMetricsType - err := res.UnmarshalGQL(v) - return res, graphql.ErrorOnPath(ctx, err) -} - -func (ec *executionContext) marshalNIngressMetricsType2githubᚗcomᚋnaisᚋapiᚋinternalᚋworkloadᚋapplicationᚐIngressMetricsType(ctx context.Context, sel ast.SelectionSet, v application.IngressMetricsType) graphql.Marshaler { - return v -} - func (ec *executionContext) unmarshalNIngressType2githubᚗcomᚋnaisᚋapiᚋinternalᚋworkloadᚋapplicationᚐIngressType(ctx context.Context, v any) (application.IngressType, error) { var res application.IngressType err := res.UnmarshalGQL(v) diff --git a/internal/graph/gengql/root_.generated.go b/internal/graph/gengql/root_.generated.go index b472fcc46..cda1b6d50 100644 --- a/internal/graph/gengql/root_.generated.go +++ b/internal/graph/gengql/root_.generated.go @@ -85,7 +85,6 @@ type ResolverRoot interface { ExternalIngressCriticalVulnerabilityIssue() ExternalIngressCriticalVulnerabilityIssueResolver FailedSynchronizationIssue() FailedSynchronizationIssueResolver Ingress() IngressResolver - IngressMetrics() IngressMetricsResolver InstanceGroup() InstanceGroupResolver InvalidSpecIssue() InvalidSpecIssueResolver IssueConnection() IssueConnectionResolver @@ -1097,20 +1096,8 @@ type ComplexityRoot struct { } Ingress struct { - Metrics func(childComplexity int) int - Type func(childComplexity int) int - URL func(childComplexity int) int - } - - IngressMetricSample struct { - Timestamp func(childComplexity int) int - Value func(childComplexity int) int - } - - IngressMetrics struct { - ErrorsPerSecond func(childComplexity int) int - RequestsPerSecond func(childComplexity int) int - Series func(childComplexity int, input application.IngressMetricsInput) int + Type func(childComplexity int) int + URL func(childComplexity int) int } InstanceGroup struct { @@ -7381,13 +7368,6 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.InboundNetworkPolicy.Rules(childComplexity), true - case "Ingress.metrics": - if e.ComplexityRoot.Ingress.Metrics == nil { - break - } - - return e.ComplexityRoot.Ingress.Metrics(childComplexity), true - case "Ingress.type": if e.ComplexityRoot.Ingress.Type == nil { break @@ -7402,46 +7382,6 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.Ingress.URL(childComplexity), true - case "IngressMetricSample.timestamp": - if e.ComplexityRoot.IngressMetricSample.Timestamp == nil { - break - } - - return e.ComplexityRoot.IngressMetricSample.Timestamp(childComplexity), true - - case "IngressMetricSample.value": - if e.ComplexityRoot.IngressMetricSample.Value == nil { - break - } - - return e.ComplexityRoot.IngressMetricSample.Value(childComplexity), true - - case "IngressMetrics.errorsPerSecond": - if e.ComplexityRoot.IngressMetrics.ErrorsPerSecond == nil { - break - } - - return e.ComplexityRoot.IngressMetrics.ErrorsPerSecond(childComplexity), true - - case "IngressMetrics.requestsPerSecond": - if e.ComplexityRoot.IngressMetrics.RequestsPerSecond == nil { - break - } - - return e.ComplexityRoot.IngressMetrics.RequestsPerSecond(childComplexity), true - - case "IngressMetrics.series": - if e.ComplexityRoot.IngressMetrics.Series == nil { - break - } - - args, err := ec.field_IngressMetrics_series_args(ctx, rawArgs) - if err != nil { - return 0, false - } - - return e.ComplexityRoot.IngressMetrics.Series(childComplexity, args["input"].(application.IngressMetricsInput)), true - case "InstanceGroup.created": if e.ComplexityRoot.InstanceGroup.Created == nil { break @@ -19276,7 +19216,6 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ec.unmarshalInputGrantPostgresAccessInput, ec.unmarshalInputImageVulnerabilityFilter, ec.unmarshalInputImageVulnerabilityOrder, - ec.unmarshalInputIngressMetricsInput, ec.unmarshalInputIssueFilter, ec.unmarshalInputIssueOrder, ec.unmarshalInputJobOrder, @@ -20828,68 +20767,6 @@ type Ingress { Type of ingress. """ type: IngressType! - - """ - Metrics for the ingress. - """ - metrics: IngressMetrics! -} - -type IngressMetrics { - """ - Number of requests to the ingress per second. - """ - requestsPerSecond: Float! - - """ - Number of errors in the ingress per second. - """ - errorsPerSecond: Float! - """ - Ingress metrics between start and end with step size. - """ - series(input: IngressMetricsInput!): [IngressMetricSample!]! -} - -input IngressMetricsInput { - """ - Fetch metrics from this timestamp. - """ - start: Time! - - """ - Fetch metrics until this timestamp. - """ - end: Time! - - """ - Type of metric to fetch. - """ - type: IngressMetricsType! -} - -""" -Type of ingress metrics to fetch. -""" -enum IngressMetricsType { - """ - Number of requests to the ingress per second. - """ - REQUESTS_PER_SECOND - """ - Number of errors in the ingress per second. - """ - ERRORS_PER_SECOND -} - -""" -Ingress metric type. -""" -type IngressMetricSample { - "Timestamp of the value." - timestamp: Time! - "Value of the IngressMetricsType at the given timestamp." - value: Float! } enum IngressType { @@ -34203,34 +34080,10 @@ func (ec *executionContext) childFields_Ingress(ctx context.Context, field graph return ec.fieldContext_Ingress_url(ctx, field) case "type": return ec.fieldContext_Ingress_type(ctx, field) - case "metrics": - return ec.fieldContext_Ingress_metrics(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type Ingress", field.Name) } -func (ec *executionContext) childFields_IngressMetricSample(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "timestamp": - return ec.fieldContext_IngressMetricSample_timestamp(ctx, field) - case "value": - return ec.fieldContext_IngressMetricSample_value(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type IngressMetricSample", field.Name) -} - -func (ec *executionContext) childFields_IngressMetrics(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "requestsPerSecond": - return ec.fieldContext_IngressMetrics_requestsPerSecond(ctx, field) - case "errorsPerSecond": - return ec.fieldContext_IngressMetrics_errorsPerSecond(ctx, field) - case "series": - return ec.fieldContext_IngressMetrics_series(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type IngressMetrics", field.Name) -} - func (ec *executionContext) childFields_InstanceGroup(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { case "id": diff --git a/internal/graph/schema/applications.graphqls b/internal/graph/schema/applications.graphqls index 4b7bf674d..25d1a010f 100644 --- a/internal/graph/schema/applications.graphqls +++ b/internal/graph/schema/applications.graphqls @@ -648,68 +648,6 @@ type Ingress { Type of ingress. """ type: IngressType! - - """ - Metrics for the ingress. - """ - metrics: IngressMetrics! -} - -type IngressMetrics { - """ - Number of requests to the ingress per second. - """ - requestsPerSecond: Float! - - """ - Number of errors in the ingress per second. - """ - errorsPerSecond: Float! - """ - Ingress metrics between start and end with step size. - """ - series(input: IngressMetricsInput!): [IngressMetricSample!]! -} - -input IngressMetricsInput { - """ - Fetch metrics from this timestamp. - """ - start: Time! - - """ - Fetch metrics until this timestamp. - """ - end: Time! - - """ - Type of metric to fetch. - """ - type: IngressMetricsType! -} - -""" -Type of ingress metrics to fetch. -""" -enum IngressMetricsType { - """ - Number of requests to the ingress per second. - """ - REQUESTS_PER_SECOND - """ - Number of errors in the ingress per second. - """ - ERRORS_PER_SECOND -} - -""" -Ingress metric type. -""" -type IngressMetricSample { - "Timestamp of the value." - timestamp: Time! - "Value of the IngressMetricsType at the given timestamp." - value: Float! } enum IngressType { diff --git a/internal/thirdparty/promclient/fake/fake_test.go b/internal/thirdparty/promclient/fake/fake_test.go index 9f36d2507..7601c9a15 100644 --- a/internal/thirdparty/promclient/fake/fake_test.go +++ b/internal/thirdparty/promclient/fake/fake_test.go @@ -213,8 +213,7 @@ func TestFakeQueryAll(t *testing.T) { t.Fatalf("failed to create watcher manager: %v", err) } - prometheusClient := NewFakeClient(nil, nil) - ctx = application.NewLoaderContext(ctx, application.NewWatcher(ctx, mgr), application.NewIngressWatcher(ctx, mgr), prometheusClient, logrus.New()) + ctx = application.NewLoaderContext(ctx, application.NewWatcher(ctx, mgr), application.NewIngressWatcher(ctx, mgr), logrus.New()) ctx = team.NewLoaderContext(ctx, pool, nil) ctxWait, cancel := context.WithTimeout(ctx, 1*time.Second) diff --git a/internal/workload/application/dataloader.go b/internal/workload/application/dataloader.go index e0166f909..bb3e9dd9f 100644 --- a/internal/workload/application/dataloader.go +++ b/internal/workload/application/dataloader.go @@ -16,8 +16,8 @@ type ctxKey int const loadersKey ctxKey = iota -func NewLoaderContext(ctx context.Context, appWatcher *watcher.Watcher[*nais_io_v1alpha1.Application], ingressWatcher *watcher.Watcher[*netv1.Ingress], client IngressMetricsClient, log logrus.FieldLogger) context.Context { - return context.WithValue(ctx, loadersKey, newLoaders(appWatcher, ingressWatcher, client, log)) +func NewLoaderContext(ctx context.Context, appWatcher *watcher.Watcher[*nais_io_v1alpha1.Application], ingressWatcher *watcher.Watcher[*netv1.Ingress], log logrus.FieldLogger) context.Context { + return context.WithValue(ctx, loadersKey, newLoaders(appWatcher, ingressWatcher, log)) } func NewWatcher(ctx context.Context, mgr *watcher.Manager) *watcher.Watcher[*nais_io_v1alpha1.Application] { @@ -39,15 +39,13 @@ func fromContext(ctx context.Context) *loaders { type loaders struct { appWatcher *watcher.Watcher[*nais_io_v1alpha1.Application] ingressWatcher *watcher.Watcher[*netv1.Ingress] - client IngressMetricsClient log logrus.FieldLogger } -func newLoaders(appWatcher *watcher.Watcher[*nais_io_v1alpha1.Application], ingressWatcher *watcher.Watcher[*netv1.Ingress], client IngressMetricsClient, log logrus.FieldLogger) *loaders { +func newLoaders(appWatcher *watcher.Watcher[*nais_io_v1alpha1.Application], ingressWatcher *watcher.Watcher[*netv1.Ingress], log logrus.FieldLogger) *loaders { return &loaders{ appWatcher: appWatcher, ingressWatcher: ingressWatcher, - client: client, log: log, } } diff --git a/internal/workload/application/models.go b/internal/workload/application/models.go index 8a8819070..8e9f18932 100644 --- a/internal/workload/application/models.go +++ b/internal/workload/application/models.go @@ -644,10 +644,6 @@ func (e IngressType) MarshalGQL(w io.Writer) { fmt.Fprint(w, strconv.Quote(e.String())) } -type IngressMetrics struct { - Ingress *Ingress `json:"-"` -} - type ApplicationInstanceStatus struct { State ApplicationInstanceState `json:"state"` Message string `json:"message"` @@ -718,75 +714,6 @@ func (e ScalingDirection) MarshalGQL(w io.Writer) { fmt.Fprint(w, strconv.Quote(e.String())) } -type IngressMetricSample struct { - Timestamp time.Time `json:"timestamp"` - Value float64 `json:"value"` -} - -type IngressMetricsInput struct { - Start time.Time `json:"start"` - End time.Time `json:"end"` - Type IngressMetricsType `json:"type"` -} - -func (w *IngressMetricsInput) Step() int { - diff := w.End.Sub(w.Start) - - switch { - case diff <= time.Hour: - return 18 - case diff <= 6*time.Hour: - return 108 - case diff <= 24*time.Hour: - return 432 - case diff <= 7*24*time.Hour: - return 1008 - default: - return 12960 - } -} - -type IngressMetricsType string - -const ( - IngressMetricsTypeRequestsPerSecond IngressMetricsType = "REQUESTS_PER_SECOND" - IngressMetricsTypeErrorsPerSecond IngressMetricsType = "ERRORS_PER_SECOND" -) - -var AllIngressMetricsType = []IngressMetricsType{ - IngressMetricsTypeRequestsPerSecond, - IngressMetricsTypeErrorsPerSecond, -} - -func (e IngressMetricsType) IsValid() bool { - switch e { - case IngressMetricsTypeRequestsPerSecond, IngressMetricsTypeErrorsPerSecond: - return true - } - return false -} - -func (e IngressMetricsType) String() string { - return string(e) -} - -func (e *IngressMetricsType) UnmarshalGQL(v any) error { - str, ok := v.(string) - if !ok { - return fmt.Errorf("enums must be strings") - } - - *e = IngressMetricsType(str) - if !e.IsValid() { - return fmt.Errorf("%s is not a valid IngressMetricsType", str) - } - return nil -} - -func (e IngressMetricsType) MarshalGQL(w io.Writer) { - fmt.Fprint(w, strconv.Quote(e.String())) -} - type ApplicationState int const ( diff --git a/internal/workload/application/prometheus.go b/internal/workload/application/prometheus.go deleted file mode 100644 index cfcc2bc8c..000000000 --- a/internal/workload/application/prometheus.go +++ /dev/null @@ -1,114 +0,0 @@ -package application - -import ( - "context" - "fmt" - "net/url" - "strings" - "time" - - "github.com/nais/api/internal/environmentmapper" - "github.com/nais/api/internal/thirdparty/promclient" - promv1 "github.com/prometheus/client_golang/api/prometheus/v1" - prom "github.com/prometheus/common/model" - "github.com/sirupsen/logrus" -) - -type IngressMetricsClient interface { - Query(ctx context.Context, environment string, query string, opts ...promclient.QueryOption) (prom.Vector, error) - QueryRange(ctx context.Context, environment string, query string, promRange promv1.Range) (prom.Value, promv1.Warnings, error) -} - -const ( - ingressRequests = `sum(rate(nginx_ingress_controller_requests{host=%q, path=%q}[2m]))` - errorRate = `sum(rate(nginx_ingress_controller_requests{status!~"^[23].*", host=%q, path=%q}[2m]))` - - errorsSeries = `sum(rate(nginx_ingress_controller_requests{host=%q, path=%q, status!~"^[23].*"}[2m]))` - requestsSeries = `sum(rate(nginx_ingress_controller_requests{host=%q, path=%q}[2m]))` -) - -func ensuredVal(v prom.Vector) float64 { - if len(v) == 0 { - return 0 - } - return float64(v[0].Value) -} - -func SeriesForIngress(ctx context.Context, obj *IngressMetrics, input IngressMetricsInput) ([]*IngressMetricSample, error) { - ingressURL, err := url.Parse(obj.Ingress.URL) - if err != nil { - return nil, fmt.Errorf("failed to parse ingress URL %q: %w", obj.Ingress.URL, err) - } - - if len(ingressURL.Path) > 1 { - ingressURL.Path = strings.TrimRight(ingressURL.Path, "/") + "(/.*)?" - } else { - ingressURL.Path = "/" - } - - query := fmt.Sprintf(errorsSeries, ingressURL.Host, ingressURL.Path) - if input.Type == IngressMetricsTypeRequestsPerSecond { - query = fmt.Sprintf(requestsSeries, ingressURL.Host, ingressURL.Path) - } - - c := fromContext(ctx).client - - v, warnings, err := c.QueryRange(ctx, environmentmapper.ClusterName(obj.Ingress.EnvironmentName), query, promv1.Range{Start: input.Start, End: input.End, Step: time.Duration(input.Step()) * time.Second}) - if err != nil { - return nil, err - } - if len(warnings) > 0 { - fromContext(ctx).log.WithFields(logrus.Fields{ - "environment": obj.Ingress.EnvironmentName, - "warnings": strings.Join(warnings, ", "), - }).Warn("prometheus query warnings") - } - - matrix, ok := v.(prom.Matrix) - if !ok { - return nil, fmt.Errorf("expected prometheus matrix, got %T", v) - } - - ret := make([]*IngressMetricSample, 0) - for _, sample := range matrix { - for _, value := range sample.Values { - ret = append(ret, &IngressMetricSample{ - Value: float64(value.Value), - Timestamp: value.Timestamp.Time(), - }) - } - } - - return ret, nil -} - -// ingressMetric finds the best-matching ingress path (longest regex match) and queries Prometheus using it. -func ingressMetric(ctx context.Context, obj *IngressMetrics, promQueryFmt string) (float64, error) { - c := fromContext(ctx).client - - ingressURL, err := url.Parse(obj.Ingress.URL) - if err != nil { - return 0, fmt.Errorf("failed to parse ingress URL %q: %w", obj.Ingress.URL, err) - } - - if len(ingressURL.Path) > 1 { - ingressURL.Path = strings.TrimRight(ingressURL.Path, "/") + "(/.*)?" - } else { - ingressURL.Path = "/" - } - - query := fmt.Sprintf(promQueryFmt, ingressURL.Host, ingressURL.Path) - a, err := c.Query(ctx, environmentmapper.ClusterName(obj.Ingress.EnvironmentName), query) - if err != nil { - return 0, fmt.Errorf("failed to query Prometheus for ingress %q: %w", obj.Ingress.URL, err) - } - return ensuredVal(a), nil -} - -func RequestsPerSecondForIngress(ctx context.Context, obj *IngressMetrics) (float64, error) { - return ingressMetric(ctx, obj, ingressRequests) -} - -func ErrorsPerSecondForIngress(ctx context.Context, obj *IngressMetrics) (float64, error) { - return ingressMetric(ctx, obj, errorRate) -}