Skip to content

Commit 5a84d40

Browse files
authored
Add proper error handling for generic app platform resource (#2113)
* Add proper error handling for generic app platform resource What This change adds proper handling of API errors into Terraform diagnostics for all app platform resources which use the generic `Resource` as their implementation. Why This makes sure that the API errors are clearly communicated to the users, as opposed to just vague "request failed on the server" messages. Signed-off-by: Igor Suleymanov <[email protected]> * Update app SDK version Signed-off-by: Igor Suleymanov <[email protected]> --------- Signed-off-by: Igor Suleymanov <[email protected]>
1 parent ea345ff commit 5a84d40

File tree

4 files changed

+204
-42
lines changed

4 files changed

+204
-42
lines changed

Diff for: go.mod

+6-6
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ require (
1313
github.com/grafana/amixr-api-go-client v0.0.20 // main branch
1414
github.com/grafana/authlib/claims v0.0.0-20250120084028-e3328c576437
1515
github.com/grafana/fleet-management-api v1.0.0
16-
github.com/grafana/grafana-app-sdk v0.34.1-0.20250314160558-ff4892e2895c
16+
github.com/grafana/grafana-app-sdk v0.35.2-0.20250408075831-c2a87bde0849
1717
github.com/grafana/grafana-com-public-clients/go/gcom v0.0.0-20250214150112-a52892176c26
1818
github.com/grafana/grafana-openapi-client-go v0.0.0-20241113095943-9cb2bbfeb8a3
1919
github.com/grafana/grafana/apps/dashboard v0.0.0-20250314125419-399df82f0b25
@@ -79,7 +79,7 @@ require (
7979
github.com/elazarl/goproxy v1.7.2 // indirect
8080
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
8181
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
82-
github.com/getkin/kin-openapi v0.129.0 // indirect
82+
github.com/getkin/kin-openapi v0.131.0 // indirect
8383
github.com/go-logr/logr v1.4.2 // indirect
8484
github.com/go-logr/stdr v1.2.2 // indirect
8585
github.com/go-openapi/analysis v0.23.0 // indirect
@@ -101,7 +101,7 @@ require (
101101
github.com/google/gofuzz v1.2.0 // indirect
102102
github.com/google/uuid v1.6.0 // indirect
103103
github.com/gorilla/mux v1.8.1 // indirect
104-
github.com/grafana/grafana-app-sdk/logging v0.33.0 // indirect
104+
github.com/grafana/grafana-app-sdk/logging v0.35.1 // indirect
105105
github.com/grafana/grafana-plugin-sdk-go v0.272.0 // indirect
106106
github.com/grafana/otel-profiling-go v0.5.1 // indirect
107107
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect
@@ -140,8 +140,8 @@ require (
140140
github.com/modern-go/reflect2 v1.0.2 // indirect
141141
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
142142
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
143-
github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80 // indirect
144-
github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 // indirect
143+
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
144+
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
145145
github.com/oklog/run v1.1.0 // indirect
146146
github.com/oklog/ulid v1.3.1 // indirect
147147
github.com/olekukonko/tablewriter v0.0.5 // indirect
@@ -197,7 +197,7 @@ require (
197197
google.golang.org/appengine v1.6.8 // indirect
198198
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a // indirect
199199
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect
200-
google.golang.org/grpc v1.71.0 // indirect
200+
google.golang.org/grpc v1.71.1 // indirect
201201
google.golang.org/protobuf v1.36.5 // indirect
202202
gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect
203203
gopkg.in/inf.v0 v0.9.1 // indirect

Diff for: go.sum

+12-12
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/
8181
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
8282
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
8383
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
84-
github.com/getkin/kin-openapi v0.129.0 h1:QGYTNcmyP5X0AtFQ2Dkou9DGBJsUETeLH9rFrJXZh30=
85-
github.com/getkin/kin-openapi v0.129.0/go.mod h1:gmWI+b/J45xqpyK5wJmRRZse5wefA5H0RDMK46kLUtI=
84+
github.com/getkin/kin-openapi v0.131.0 h1:NO2UeHnFKRYhZ8wg6Nyh5Cq7dHk4suQQr72a4pMrDxE=
85+
github.com/getkin/kin-openapi v0.131.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58=
8686
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
8787
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
8888
github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8=
@@ -163,10 +163,10 @@ github.com/grafana/authlib/claims v0.0.0-20250120084028-e3328c576437 h1:OlwbIVFc
163163
github.com/grafana/authlib/claims v0.0.0-20250120084028-e3328c576437/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A=
164164
github.com/grafana/fleet-management-api v1.0.0 h1:twb0kBgeNRcvQi7iDXq7AFhlM2mWcN76kTXleJuPA38=
165165
github.com/grafana/fleet-management-api v1.0.0/go.mod h1:6iJjhjWhHZ8iwkyuDXFVTuay57JILnE3kaOPk8Nzorw=
166-
github.com/grafana/grafana-app-sdk v0.34.1-0.20250314160558-ff4892e2895c h1:b6fCco//2H4kIL6fdDbKiNV0OoxLmu6uK46Vxjn66tI=
167-
github.com/grafana/grafana-app-sdk v0.34.1-0.20250314160558-ff4892e2895c/go.mod h1:KsYr7tQC4Pk//MLKiFlPpYoZDyOeN81zlc9e1tfMuwE=
168-
github.com/grafana/grafana-app-sdk/logging v0.33.0 h1:7ujiu5iuLcGe9ODpKlvkpOm/xpYImDxFdun7j8dw04w=
169-
github.com/grafana/grafana-app-sdk/logging v0.33.0/go.mod h1:Y/bvbDhBiV/tkIle9RW49pgfSPIPSON8Q4qjx3pyqDk=
166+
github.com/grafana/grafana-app-sdk v0.35.2-0.20250408075831-c2a87bde0849 h1:fzoG0CxBUwrACCiOKrKZ0q2fKt5QYjlxyrD74rAnxps=
167+
github.com/grafana/grafana-app-sdk v0.35.2-0.20250408075831-c2a87bde0849/go.mod h1:hQFtfn65wd/zTcQ/ftwOpRTr/B37MfIzPZD8jGoAx5E=
168+
github.com/grafana/grafana-app-sdk/logging v0.35.1 h1:taVpl+RoixTYl0JBJGhH+fPVmwA9wvdwdzJTZsv9buM=
169+
github.com/grafana/grafana-app-sdk/logging v0.35.1/go.mod h1:Y/bvbDhBiV/tkIle9RW49pgfSPIPSON8Q4qjx3pyqDk=
170170
github.com/grafana/grafana-com-public-clients/go/gcom v0.0.0-20250214150112-a52892176c26 h1:7NMB6/x0CcfH/zKQ5D+3Ffb2DbYMJBx0QdJ1GGdw8z4=
171171
github.com/grafana/grafana-com-public-clients/go/gcom v0.0.0-20250214150112-a52892176c26/go.mod h1:sYWkB3NhyirQJfy3wtNQ29UYjoHbRlJlYhqN1jNsC5g=
172172
github.com/grafana/grafana-openapi-client-go v0.0.0-20241113095943-9cb2bbfeb8a3 h1:poKxGlUaEYVp2DMofC/I2GHw/vvtHAZ20c48I8rFB6M=
@@ -332,10 +332,10 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9
332332
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
333333
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
334334
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
335-
github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80 h1:nZspmSkneBbtxU9TopEAE0CY+SBJLxO8LPUlw2vG4pU=
336-
github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80/go.mod h1:7tFDb+Y51LcDpn26GccuUgQXUk6t0CXZsivKjyimYX8=
337-
github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 h1:t05Ww3DxZutOqbMN+7OIuqDwXbhl32HiZGpLy26BAPc=
338-
github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o=
335+
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY=
336+
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw=
337+
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c=
338+
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o=
339339
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
340340
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
341341
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
@@ -578,8 +578,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a h1:
578578
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go.mod h1:3kWAYMk1I75K4vykHtKt2ycnOgpA6974V7bREqbsenU=
579579
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4=
580580
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ=
581-
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
582-
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
581+
google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI=
582+
google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
583583
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
584584
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
585585
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=

Diff for: internal/resources/appplatform/errors.go

+165
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
package appplatform
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"strconv"
7+
"strings"
8+
9+
"github.com/hashicorp/terraform-plugin-framework/diag"
10+
"github.com/hashicorp/terraform-plugin-framework/path"
11+
apierrors "k8s.io/apimachinery/pkg/api/errors"
12+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
13+
)
14+
15+
// ResourceAction is the action that is being performed on the resource.
16+
type ResourceAction string
17+
18+
const (
19+
ResourceActionCreate ResourceAction = "create"
20+
ResourceActionUpdate ResourceAction = "update"
21+
ResourceActionDelete ResourceAction = "delete"
22+
ResourceActionRead ResourceAction = "read"
23+
)
24+
25+
// ErrorToDiagnostics formats an error from the Kubernetes API into Terraform diagnostics.
26+
func ErrorToDiagnostics(action ResourceAction, resourceName, resourceType string, err error) diag.Diagnostics {
27+
res := make(diag.Diagnostics, 0)
28+
29+
var serr apierrors.APIStatus
30+
if status, ok := err.(apierrors.APIStatus); ok || errors.As(err, &status) {
31+
serr = status
32+
} else {
33+
res.AddError(
34+
fmt.Sprintf("failed to %s resource %q: unknown error", action, resourceName),
35+
err.Error(),
36+
)
37+
38+
return res
39+
}
40+
41+
status := serr.Status()
42+
switch status.Reason {
43+
case metav1.StatusReasonInvalid:
44+
if len(status.Details.Causes) > 0 {
45+
errs := FieldErrorsFromCauses(resourceType, status.Details.Causes)
46+
47+
for _, err := range errs {
48+
var msg strings.Builder
49+
for i, m := range err.Messages {
50+
msg.WriteString("* ")
51+
msg.WriteString(m)
52+
53+
// Don't add a newline after the last message.
54+
if i < len(err.Messages)-1 {
55+
msg.WriteString("\n")
56+
}
57+
}
58+
59+
res.AddAttributeError(err.Path, fmt.Sprintf("invalid value for field \"%s\":", err.Field), msg.String())
60+
}
61+
} else {
62+
res.AddError(
63+
fmt.Sprintf(
64+
"failed to %s resource %q: HTTP %d - %s", action, resourceName, status.Code, metav1.StatusReasonInvalid,
65+
),
66+
status.Message,
67+
)
68+
}
69+
case metav1.StatusReasonUnknown:
70+
res.AddError(
71+
fmt.Sprintf("failed to %s resource %q: HTTP %d - Unknown", action, resourceName, status.Code),
72+
status.Message,
73+
)
74+
default:
75+
res.AddError(
76+
fmt.Sprintf("failed to %s resource %q: HTTP %d - %s", action, resourceName, status.Code, status.Reason),
77+
status.Message,
78+
)
79+
}
80+
81+
return res
82+
}
83+
84+
// FieldError is a field-level error returned by the Kubernetes API,
85+
// with the path and messages adapted to Terraform diagnostics.
86+
type FieldError struct {
87+
Field string
88+
Path path.Path
89+
Messages []string
90+
}
91+
92+
// FieldErrors is a map of field names to FieldError instances.
93+
type FieldErrors map[string]FieldError
94+
95+
// FieldErrorsFromCauses converts a list of field errors returned by the Kubernetes API
96+
// into a map of Terraform-acceptable field-level errors.
97+
func FieldErrorsFromCauses(resourceType string, causes []metav1.StatusCause) FieldErrors {
98+
res := make(FieldErrors, len(causes))
99+
100+
for _, cause := range causes {
101+
v, ok := res[cause.Field]
102+
103+
if ok {
104+
v.Messages = append(v.Messages, cause.Message)
105+
} else {
106+
v = FieldError{
107+
Field: cause.Field,
108+
Path: ParseFieldPath(resourceType, cause.Field),
109+
Messages: []string{cause.Message},
110+
}
111+
}
112+
113+
res[cause.Field] = v
114+
}
115+
116+
return res
117+
}
118+
119+
// ParseFieldPath converts a Kubernetes field path into a Terraform path.
120+
func ParseFieldPath(resourceType, fieldPath string) path.Path {
121+
parts := strings.Split(fieldPath, ".")
122+
123+
if len(parts) == 0 {
124+
return path.Root("")
125+
}
126+
127+
var res path.Path
128+
switch parts[0] {
129+
case "metadata":
130+
res = path.Root("metadata")
131+
case "spec":
132+
res = path.Root("spec")
133+
default:
134+
// unknown, not supported
135+
return path.Root("")
136+
}
137+
138+
if
139+
// TODO (@radiohead): this is a hack because the dashboard spec relies on the json field.
140+
// We need to find a better way to handle this.
141+
resourceType == "grafana_apps_dashboard_dashboard_v1alpha1" &&
142+
res.Equal(path.Root("spec")) &&
143+
parts[1] != "title" &&
144+
parts[1] != "tags" {
145+
res = res.AtName("json")
146+
147+
for _, part := range parts[1:] {
148+
if idx, err := strconv.Atoi(part); err == nil {
149+
res = res.AtListIndex(idx)
150+
} else {
151+
res = res.AtMapKey(part)
152+
}
153+
}
154+
} else {
155+
for _, part := range parts[1:] {
156+
if idx, err := strconv.Atoi(part); err == nil {
157+
res = res.AtListIndex(idx)
158+
} else {
159+
res = res.AtName(part)
160+
}
161+
}
162+
}
163+
164+
return res
165+
}

Diff for: internal/resources/appplatform/resource.go

+21-24
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,10 @@ package appplatform
22

33
import (
44
"context"
5-
"errors"
65
"fmt"
7-
"net/http"
86
"strings"
97

108
"github.com/grafana/authlib/claims"
11-
"github.com/grafana/grafana-app-sdk/k8s"
129
sdkresource "github.com/grafana/grafana-app-sdk/resource"
1310
"github.com/grafana/grafana/pkg/apimachinery/utils"
1411
"github.com/hashicorp/terraform-plugin-framework/attr"
@@ -19,6 +16,7 @@ import (
1916
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
2017
"github.com/hashicorp/terraform-plugin-framework/types"
2118
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
19+
apierrors "k8s.io/apimachinery/pkg/api/errors"
2220
k8stypes "k8s.io/apimachinery/pkg/types"
2321

2422
"github.com/grafana/terraform-provider-grafana/v3/internal/common"
@@ -63,25 +61,23 @@ type ResourceSpecSchema struct {
6361

6462
// Resource is a generic Terraform resource for a Grafana resource.
6563
type Resource[T sdkresource.Object, L sdkresource.ListObject] struct {
66-
config ResourceConfig[T]
67-
client *sdkresource.NamespacedClient[T, L]
68-
clientID string
64+
config ResourceConfig[T]
65+
client *sdkresource.NamespacedClient[T, L]
66+
clientID string
67+
resourceName string
6968
}
7069

7170
// NewResource creates a new Terraform resource for a Grafana resource.
7271
func NewResource[T sdkresource.Object, L sdkresource.ListObject](cfg ResourceConfig[T]) resource.Resource {
7372
return &Resource[T, L]{
74-
config: cfg,
73+
config: cfg,
74+
resourceName: formatResourceType(cfg.Kind),
7575
}
7676
}
7777

7878
// Metadata returns the metadata for the Resource.
7979
func (r *Resource[T, L]) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
80-
// TODO: extract & validate in the constructor instead,
81-
// because we need to make sure that the group has the proper format.
82-
g := strings.Split(r.config.Kind.Group(), ".")[0]
83-
84-
resp.TypeName = fmt.Sprintf("grafana_apps_%s_%s_%s", g, strings.ToLower(r.config.Kind.Kind()), r.config.Kind.Version())
80+
resp.TypeName = r.resourceName
8581
}
8682

8783
// Schema returns the schema for the Resource.
@@ -235,12 +231,12 @@ func (r *Resource[T, L]) Read(ctx context.Context, req resource.ReadRequest, res
235231

236232
res, err := r.client.Get(ctx, obj.GetName())
237233
if err != nil {
238-
if isNotFoundErr(err) {
234+
if apierrors.IsNotFound(err) {
239235
resp.State.RemoveResource(ctx)
240236
return
241237
}
242238

243-
resp.Diagnostics.AddError("failed to get resource", err.Error())
239+
resp.Diagnostics.Append(ErrorToDiagnostics(ResourceActionRead, obj.GetName(), r.resourceName, err)...)
244240
return
245241
}
246242

@@ -291,7 +287,7 @@ func (r *Resource[T, L]) Create(ctx context.Context, req resource.CreateRequest,
291287

292288
res, err := r.client.Create(ctx, obj, sdkresource.CreateOptions{})
293289
if err != nil {
294-
resp.Diagnostics.AddError("failed to create resource", err.Error())
290+
resp.Diagnostics.Append(ErrorToDiagnostics(ResourceActionCreate, obj.GetName(), r.resourceName, err)...)
295291
return
296292
}
297293

@@ -348,7 +344,7 @@ func (r *Resource[T, L]) Update(ctx context.Context, req resource.UpdateRequest,
348344

349345
res, err := r.client.Update(ctx, obj, reqopts)
350346
if err != nil {
351-
resp.Diagnostics.AddError("failed to update resource", err.Error())
347+
resp.Diagnostics.Append(ErrorToDiagnostics(ResourceActionUpdate, obj.GetName(), r.resourceName, err)...)
352348
return
353349
}
354350

@@ -393,7 +389,11 @@ func (r *Resource[T, L]) Delete(ctx context.Context, req resource.DeleteRequest,
393389
}
394390

395391
if err := r.client.Delete(ctx, obj.GetName(), sdkresource.DeleteOptions{}); err != nil {
396-
resp.Diagnostics.AddError("failed to delete resource", err.Error())
392+
if apierrors.IsNotFound(err) {
393+
return
394+
}
395+
396+
resp.Diagnostics.Append(ErrorToDiagnostics(ResourceActionDelete, obj.GetName(), r.resourceName, err)...)
397397
return
398398
}
399399
}
@@ -402,7 +402,7 @@ func (r *Resource[T, L]) Delete(ctx context.Context, req resource.DeleteRequest,
402402
func (r *Resource[T, L]) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
403403
res, err := r.client.Get(ctx, req.ID)
404404
if err != nil {
405-
resp.Diagnostics.AddError("failed to get resource", err.Error())
405+
resp.Diagnostics.Append(ErrorToDiagnostics(ResourceActionRead, req.ID, r.resourceName, err)...)
406406
return
407407
}
408408

@@ -611,10 +611,7 @@ func setManagerProperties(obj sdkresource.Object, clientID string) error {
611611
return nil
612612
}
613613

614-
func isNotFoundErr(err error) bool {
615-
var cast *k8s.ServerResponseError
616-
if errors.As(err, &cast) {
617-
return cast.StatusCode() == http.StatusNotFound
618-
}
619-
return false
614+
func formatResourceType(kind sdkresource.Kind) string {
615+
g := strings.Split(kind.Group(), ".")[0]
616+
return fmt.Sprintf("grafana_apps_%s_%s_%s", g, strings.ToLower(kind.Kind()), kind.Version())
620617
}

0 commit comments

Comments
 (0)