client.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  1. /*
  2. Licensed under the Apache License, Version 2.0 (the "License");
  3. you may not use this file except in compliance with the License.
  4. You may obtain a copy of the License at
  5. http://www.apache.org/licenses/LICENSE-2.0
  6. Unless required by applicable law or agreed to in writing, software
  7. distributed under the License is distributed on an "AS IS" BASIS,
  8. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. See the License for the specific language governing permissions and
  10. limitations under the License.
  11. */
  12. package secretmanager
  13. import (
  14. "context"
  15. "encoding/json"
  16. "errors"
  17. "fmt"
  18. "maps"
  19. "strconv"
  20. "strings"
  21. secretmanager "cloud.google.com/go/secretmanager/apiv1"
  22. "cloud.google.com/go/secretmanager/apiv1/secretmanagerpb"
  23. "github.com/googleapis/gax-go/v2"
  24. "github.com/googleapis/gax-go/v2/apierror"
  25. "github.com/tidwall/gjson"
  26. "google.golang.org/api/iterator"
  27. "google.golang.org/genproto/protobuf/field_mask"
  28. "google.golang.org/grpc/codes"
  29. "google.golang.org/grpc/status"
  30. corev1 "k8s.io/api/core/v1"
  31. ctrl "sigs.k8s.io/controller-runtime"
  32. kclient "sigs.k8s.io/controller-runtime/pkg/client"
  33. esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
  34. "github.com/external-secrets/external-secrets/pkg/constants"
  35. "github.com/external-secrets/external-secrets/pkg/find"
  36. "github.com/external-secrets/external-secrets/pkg/metrics"
  37. "github.com/external-secrets/external-secrets/pkg/provider/util/locks"
  38. "github.com/external-secrets/external-secrets/pkg/utils"
  39. )
  40. const (
  41. CloudPlatformRole = "https://www.googleapis.com/auth/cloud-platform"
  42. defaultVersion = "latest"
  43. errGCPSMStore = "received invalid GCPSM SecretStore resource"
  44. errUnableGetCredentials = "unable to get credentials: %w"
  45. errClientClose = "unable to close SecretManager client: %w"
  46. errUnableProcessJSONCredentials = "failed to process the provided JSON credentials: %w"
  47. errUnableCreateGCPSMClient = "failed to create GCP secretmanager client: %w"
  48. errUninitalizedGCPProvider = "provider GCP is not initialized"
  49. errClientGetSecretAccess = "unable to access Secret from SecretManager Client: %w"
  50. errJSONSecretUnmarshal = "unable to unmarshal secret: %w"
  51. errInvalidStore = "invalid store"
  52. errInvalidStoreSpec = "invalid store spec"
  53. errInvalidStoreProv = "invalid store provider"
  54. errInvalidGCPProv = "invalid gcp secrets manager provider"
  55. errInvalidAuthSecretRef = "invalid auth secret data: %w"
  56. errInvalidWISARef = "invalid workload identity service account reference: %w"
  57. errUnexpectedFindOperator = "unexpected find operator"
  58. managedByKey = "managed-by"
  59. managedByValue = "external-secrets"
  60. providerName = "GCPSecretManager"
  61. )
  62. type Client struct {
  63. smClient GoogleSecretManagerClient
  64. kube kclient.Client
  65. store *esv1beta1.GCPSMProvider
  66. storeKind string
  67. // namespace of the external secret
  68. namespace string
  69. workloadIdentity *workloadIdentity
  70. }
  71. type GoogleSecretManagerClient interface {
  72. DeleteSecret(ctx context.Context, req *secretmanagerpb.DeleteSecretRequest, opts ...gax.CallOption) error
  73. AccessSecretVersion(ctx context.Context, req *secretmanagerpb.AccessSecretVersionRequest, opts ...gax.CallOption) (*secretmanagerpb.AccessSecretVersionResponse, error)
  74. ListSecrets(ctx context.Context, req *secretmanagerpb.ListSecretsRequest, opts ...gax.CallOption) *secretmanager.SecretIterator
  75. AddSecretVersion(ctx context.Context, req *secretmanagerpb.AddSecretVersionRequest, opts ...gax.CallOption) (*secretmanagerpb.SecretVersion, error)
  76. CreateSecret(ctx context.Context, req *secretmanagerpb.CreateSecretRequest, opts ...gax.CallOption) (*secretmanagerpb.Secret, error)
  77. Close() error
  78. GetSecret(ctx context.Context, req *secretmanagerpb.GetSecretRequest, opts ...gax.CallOption) (*secretmanagerpb.Secret, error)
  79. UpdateSecret(context.Context, *secretmanagerpb.UpdateSecretRequest, ...gax.CallOption) (*secretmanagerpb.Secret, error)
  80. }
  81. var log = ctrl.Log.WithName("provider").WithName("gcp").WithName("secretsmanager")
  82. func (c *Client) DeleteSecret(ctx context.Context, remoteRef esv1beta1.PushSecretRemoteRef) error {
  83. gcpSecret, err := c.smClient.GetSecret(ctx, &secretmanagerpb.GetSecretRequest{
  84. Name: fmt.Sprintf("projects/%s/secrets/%s", c.store.ProjectID, remoteRef.GetRemoteKey()),
  85. })
  86. metrics.ObserveAPICall(constants.ProviderGCPSM, constants.CallGCPSMGetSecret, err)
  87. if err != nil {
  88. if status.Code(err) == codes.NotFound {
  89. return nil
  90. }
  91. return err
  92. }
  93. if manager, ok := gcpSecret.Labels[managedByKey]; !ok || manager != managedByValue {
  94. return nil
  95. }
  96. deleteSecretVersionReq := &secretmanagerpb.DeleteSecretRequest{
  97. Name: fmt.Sprintf("projects/%s/secrets/%s", c.store.ProjectID, remoteRef.GetRemoteKey()),
  98. Etag: gcpSecret.Etag,
  99. }
  100. err = c.smClient.DeleteSecret(ctx, deleteSecretVersionReq)
  101. metrics.ObserveAPICall(constants.ProviderGCPSM, constants.CallGCPSMDeleteSecret, err)
  102. return err
  103. }
  104. func parseError(err error) error {
  105. var gerr *apierror.APIError
  106. if errors.As(err, &gerr) && gerr.GRPCStatus().Code() == codes.NotFound {
  107. return esv1beta1.NoSecretError{}
  108. }
  109. return err
  110. }
  111. func (c *Client) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
  112. return false, errors.New("not implemented")
  113. }
  114. // PushSecret pushes a kubernetes secret key into gcp provider Secret.
  115. func (c *Client) PushSecret(ctx context.Context, secret *corev1.Secret, pushSecretData esv1beta1.PushSecretData) error {
  116. var (
  117. payload []byte
  118. err error
  119. )
  120. if pushSecretData.GetSecretKey() == "" {
  121. // Must convert secret values to string, otherwise data will be sent as base64 to Vault
  122. secretStringVal := make(map[string]string)
  123. for k, v := range secret.Data {
  124. secretStringVal[k] = string(v)
  125. }
  126. payload, err = utils.JSONMarshal(secretStringVal)
  127. if err != nil {
  128. return fmt.Errorf("failed to serialize secret content as JSON: %w", err)
  129. }
  130. } else {
  131. payload = secret.Data[pushSecretData.GetSecretKey()]
  132. }
  133. secretName := fmt.Sprintf("projects/%s/secrets/%s", c.store.ProjectID, pushSecretData.GetRemoteKey())
  134. gcpSecret, err := c.smClient.GetSecret(ctx, &secretmanagerpb.GetSecretRequest{
  135. Name: secretName,
  136. })
  137. metrics.ObserveAPICall(constants.ProviderGCPSM, constants.CallGCPSMGetSecret, err)
  138. if err != nil {
  139. if status.Code(err) != codes.NotFound {
  140. return err
  141. }
  142. var replication = &secretmanagerpb.Replication{
  143. Replication: &secretmanagerpb.Replication_Automatic_{
  144. Automatic: &secretmanagerpb.Replication_Automatic{},
  145. },
  146. }
  147. if c.store.Location != "" {
  148. replication = &secretmanagerpb.Replication{
  149. Replication: &secretmanagerpb.Replication_UserManaged_{
  150. UserManaged: &secretmanagerpb.Replication_UserManaged{
  151. Replicas: []*secretmanagerpb.Replication_UserManaged_Replica{
  152. {
  153. Location: c.store.Location,
  154. },
  155. },
  156. },
  157. },
  158. }
  159. }
  160. gcpSecret, err = c.smClient.CreateSecret(ctx, &secretmanagerpb.CreateSecretRequest{
  161. Parent: fmt.Sprintf("projects/%s", c.store.ProjectID),
  162. SecretId: pushSecretData.GetRemoteKey(),
  163. Secret: &secretmanagerpb.Secret{
  164. Labels: map[string]string{
  165. managedByKey: managedByValue,
  166. },
  167. Replication: replication,
  168. },
  169. })
  170. metrics.ObserveAPICall(constants.ProviderGCPSM, constants.CallGCPSMCreateSecret, err)
  171. if err != nil {
  172. return err
  173. }
  174. }
  175. builder, err := newPushSecretBuilder(payload, pushSecretData)
  176. if err != nil {
  177. return err
  178. }
  179. annotations, labels, err := builder.buildMetadata(gcpSecret.Annotations, gcpSecret.Labels)
  180. if err != nil {
  181. return err
  182. }
  183. if !maps.Equal(gcpSecret.Annotations, annotations) || !maps.Equal(gcpSecret.Labels, labels) {
  184. scrt := &secretmanagerpb.Secret{
  185. Name: gcpSecret.Name,
  186. Etag: gcpSecret.Etag,
  187. Labels: labels,
  188. Annotations: annotations,
  189. }
  190. if c.store.Location != "" {
  191. scrt.Replication = &secretmanagerpb.Replication{
  192. Replication: &secretmanagerpb.Replication_UserManaged_{
  193. UserManaged: &secretmanagerpb.Replication_UserManaged{
  194. Replicas: []*secretmanagerpb.Replication_UserManaged_Replica{
  195. {
  196. Location: c.store.Location,
  197. },
  198. },
  199. },
  200. },
  201. }
  202. }
  203. _, err = c.smClient.UpdateSecret(ctx, &secretmanagerpb.UpdateSecretRequest{
  204. Secret: scrt,
  205. UpdateMask: &field_mask.FieldMask{
  206. Paths: []string{"labels", "annotations"},
  207. },
  208. })
  209. metrics.ObserveAPICall(constants.ProviderGCPSM, constants.CallGCPSMUpdateSecret, err)
  210. if err != nil {
  211. return err
  212. }
  213. }
  214. unlock, err := locks.TryLock(providerName, secretName)
  215. if err != nil {
  216. return err
  217. }
  218. defer unlock()
  219. gcpVersion, err := c.smClient.AccessSecretVersion(ctx, &secretmanagerpb.AccessSecretVersionRequest{
  220. Name: fmt.Sprintf("%s/versions/latest", secretName),
  221. })
  222. metrics.ObserveAPICall(constants.ProviderGCPSM, constants.CallGCPSMAccessSecretVersion, err)
  223. if err != nil && status.Code(err) != codes.NotFound {
  224. return err
  225. }
  226. if gcpVersion != nil && gcpVersion.Payload != nil && !builder.needUpdate(gcpVersion.Payload.Data) {
  227. return nil
  228. }
  229. var original []byte
  230. if gcpVersion != nil && gcpVersion.Payload != nil {
  231. original = gcpVersion.Payload.Data
  232. }
  233. data, err := builder.buildData(original)
  234. if err != nil {
  235. return err
  236. }
  237. addSecretVersionReq := &secretmanagerpb.AddSecretVersionRequest{
  238. Parent: fmt.Sprintf("projects/%s/secrets/%s", c.store.ProjectID, pushSecretData.GetRemoteKey()),
  239. Payload: &secretmanagerpb.SecretPayload{
  240. Data: data,
  241. },
  242. }
  243. _, err = c.smClient.AddSecretVersion(ctx, addSecretVersionReq)
  244. metrics.ObserveAPICall(constants.ProviderGCPSM, constants.CallGCPSMAddSecretVersion, err)
  245. return err
  246. }
  247. // GetAllSecrets syncs multiple secrets from gcp provider into a single Kubernetes Secret.
  248. func (c *Client) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
  249. if ref.Name != nil {
  250. return c.findByName(ctx, ref)
  251. }
  252. if len(ref.Tags) > 0 {
  253. return c.findByTags(ctx, ref)
  254. }
  255. return nil, errors.New(errUnexpectedFindOperator)
  256. }
  257. func (c *Client) findByName(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
  258. // regex matcher
  259. matcher, err := find.New(*ref.Name)
  260. if err != nil {
  261. return nil, err
  262. }
  263. req := &secretmanagerpb.ListSecretsRequest{
  264. Parent: fmt.Sprintf("projects/%s", c.store.ProjectID),
  265. }
  266. if ref.Path != nil {
  267. req.Filter = fmt.Sprintf("name:%s", *ref.Path)
  268. }
  269. // Call the API.
  270. it := c.smClient.ListSecrets(ctx, req)
  271. secretMap := make(map[string][]byte)
  272. var resp *secretmanagerpb.Secret
  273. defer metrics.ObserveAPICall(constants.ProviderGCPSM, constants.CallGCPSMListSecrets, err)
  274. for {
  275. resp, err = it.Next()
  276. if errors.Is(err, iterator.Done) {
  277. break
  278. }
  279. if err != nil {
  280. return nil, fmt.Errorf("failed to list secrets: %w", err)
  281. }
  282. log.V(1).Info("gcp sm findByName found", "secrets", strconv.Itoa(it.PageInfo().Remaining()))
  283. key := c.trimName(resp.Name)
  284. // If we don't match we skip.
  285. // Also, if we have path, and it is not at the beguining we skip.
  286. // We have to check if path is at the beguining of the key because
  287. // there is no way to create a `name:%s*` (starts with) filter
  288. // At https://cloud.google.com/secret-manager/docs/filtering you can use `*`
  289. // but not like that it seems.
  290. if !matcher.MatchName(key) || (ref.Path != nil && !strings.HasPrefix(key, *ref.Path)) {
  291. continue
  292. }
  293. log.V(1).Info("gcp sm findByName matches", "name", resp.Name)
  294. secretMap[key], err = c.getData(ctx, key)
  295. if err != nil {
  296. return nil, err
  297. }
  298. }
  299. return utils.ConvertKeys(ref.ConversionStrategy, secretMap)
  300. }
  301. func (c *Client) getData(ctx context.Context, key string) ([]byte, error) {
  302. dataRef := esv1beta1.ExternalSecretDataRemoteRef{
  303. Key: key,
  304. }
  305. data, err := c.GetSecret(ctx, dataRef)
  306. if err != nil {
  307. return []byte(""), err
  308. }
  309. return data, nil
  310. }
  311. func (c *Client) findByTags(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
  312. var tagFilter string
  313. for k, v := range ref.Tags {
  314. tagFilter = fmt.Sprintf("%slabels.%s=%s ", tagFilter, k, v)
  315. }
  316. tagFilter = strings.TrimSuffix(tagFilter, " ")
  317. if ref.Path != nil {
  318. tagFilter = fmt.Sprintf("%s name:%s", tagFilter, *ref.Path)
  319. }
  320. req := &secretmanagerpb.ListSecretsRequest{
  321. Parent: fmt.Sprintf("projects/%s", c.store.ProjectID),
  322. }
  323. log.V(1).Info("gcp sm findByTags", "tagFilter", tagFilter)
  324. req.Filter = tagFilter
  325. // Call the API.
  326. it := c.smClient.ListSecrets(ctx, req)
  327. var resp *secretmanagerpb.Secret
  328. var err error
  329. defer metrics.ObserveAPICall(constants.ProviderGCPSM, constants.CallGCPSMListSecrets, err)
  330. secretMap := make(map[string][]byte)
  331. for {
  332. resp, err = it.Next()
  333. if errors.Is(err, iterator.Done) {
  334. break
  335. }
  336. if err != nil {
  337. return nil, fmt.Errorf("failed to list secrets: %w", err)
  338. }
  339. key := c.trimName(resp.Name)
  340. if ref.Path != nil && !strings.HasPrefix(key, *ref.Path) {
  341. continue
  342. }
  343. log.V(1).Info("gcp sm findByTags matches tags", "name", resp.Name)
  344. secretMap[key], err = c.getData(ctx, key)
  345. if err != nil {
  346. return nil, err
  347. }
  348. }
  349. return utils.ConvertKeys(ref.ConversionStrategy, secretMap)
  350. }
  351. func (c *Client) trimName(name string) string {
  352. projectIDNumuber := c.extractProjectIDNumber(name)
  353. key := strings.TrimPrefix(name, fmt.Sprintf("projects/%s/secrets/", projectIDNumuber))
  354. return key
  355. }
  356. // extractProjectIDNumber grabs the project id from the full name returned by gcp api
  357. // gcp api seems to always return the number and not the project name
  358. // (and users would always use the name, while requests accept both).
  359. func (c *Client) extractProjectIDNumber(secretFullName string) string {
  360. s := strings.Split(secretFullName, "/")
  361. ProjectIDNumuber := s[1]
  362. return ProjectIDNumuber
  363. }
  364. // GetSecret returns a single secret from the provider.
  365. func (c *Client) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  366. if utils.IsNil(c.smClient) || c.store.ProjectID == "" {
  367. return nil, errors.New(errUninitalizedGCPProvider)
  368. }
  369. if ref.MetadataPolicy == esv1beta1.ExternalSecretMetadataPolicyFetch {
  370. return c.getSecretMetadata(ctx, ref)
  371. }
  372. version := ref.Version
  373. if version == "" {
  374. version = defaultVersion
  375. }
  376. req := &secretmanagerpb.AccessSecretVersionRequest{
  377. Name: fmt.Sprintf("projects/%s/secrets/%s/versions/%s", c.store.ProjectID, ref.Key, version),
  378. }
  379. result, err := c.smClient.AccessSecretVersion(ctx, req)
  380. metrics.ObserveAPICall(constants.ProviderGCPSM, constants.CallGCPSMAccessSecretVersion, err)
  381. err = parseError(err)
  382. if err != nil {
  383. return nil, fmt.Errorf(errClientGetSecretAccess, err)
  384. }
  385. if ref.Property == "" {
  386. if result.Payload.Data != nil {
  387. return result.Payload.Data, nil
  388. }
  389. return nil, fmt.Errorf("invalid secret received. no secret string for key: %s", ref.Key)
  390. }
  391. val := getDataByProperty(result.Payload.Data, ref.Property)
  392. if !val.Exists() {
  393. return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
  394. }
  395. return []byte(val.String()), nil
  396. }
  397. func (c *Client) getSecretMetadata(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  398. secret, err := c.smClient.GetSecret(ctx, &secretmanagerpb.GetSecretRequest{
  399. Name: fmt.Sprintf("projects/%s/secrets/%s", c.store.ProjectID, ref.Key),
  400. })
  401. err = parseError(err)
  402. if err != nil {
  403. return nil, fmt.Errorf(errClientGetSecretAccess, err)
  404. }
  405. const (
  406. annotations = "annotations"
  407. labels = "labels"
  408. )
  409. extractMetadataKey := func(s string, p string) string {
  410. prefix := p + "."
  411. if !strings.HasPrefix(s, prefix) {
  412. return ""
  413. }
  414. return strings.TrimPrefix(s, prefix)
  415. }
  416. if annotation := extractMetadataKey(ref.Property, annotations); annotation != "" {
  417. v, ok := secret.GetAnnotations()[annotation]
  418. if !ok {
  419. return nil, fmt.Errorf("annotation with key %s does not exist in secret %s", annotation, ref.Key)
  420. }
  421. return []byte(v), nil
  422. }
  423. if label := extractMetadataKey(ref.Property, labels); label != "" {
  424. v, ok := secret.GetLabels()[label]
  425. if !ok {
  426. return nil, fmt.Errorf("label with key %s does not exist in secret %s", label, ref.Key)
  427. }
  428. return []byte(v), nil
  429. }
  430. if ref.Property == annotations {
  431. j, err := json.Marshal(secret.GetAnnotations())
  432. if err != nil {
  433. return nil, fmt.Errorf("faild marshaling annotations into json: %w", err)
  434. }
  435. return j, nil
  436. }
  437. if ref.Property == labels {
  438. j, err := json.Marshal(secret.GetLabels())
  439. if err != nil {
  440. return nil, fmt.Errorf("faild marshaling labels into json: %w", err)
  441. }
  442. return j, nil
  443. }
  444. if ref.Property != "" {
  445. return nil, fmt.Errorf("invalid property %s: metadata property should start with either %s or %s", ref.Property, annotations, labels)
  446. }
  447. j, err := json.Marshal(map[string]map[string]string{
  448. "annotations": secret.GetAnnotations(),
  449. "labels": secret.GetLabels(),
  450. })
  451. if err != nil {
  452. return nil, fmt.Errorf("faild marshaling metadata map into json: %w", err)
  453. }
  454. return j, nil
  455. }
  456. // GetSecretMap returns multiple k/v pairs from the provider.
  457. func (c *Client) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  458. if c.smClient == nil || c.store.ProjectID == "" {
  459. return nil, errors.New(errUninitalizedGCPProvider)
  460. }
  461. data, err := c.GetSecret(ctx, ref)
  462. if err != nil {
  463. return nil, err
  464. }
  465. kv := make(map[string]json.RawMessage)
  466. err = json.Unmarshal(data, &kv)
  467. if err != nil {
  468. return nil, fmt.Errorf(errJSONSecretUnmarshal, err)
  469. }
  470. secretData := make(map[string][]byte)
  471. for k, v := range kv {
  472. var strVal string
  473. err = json.Unmarshal(v, &strVal)
  474. if err == nil {
  475. secretData[k] = []byte(strVal)
  476. } else {
  477. secretData[k] = v
  478. }
  479. }
  480. return secretData, nil
  481. }
  482. func (c *Client) Close(_ context.Context) error {
  483. var err error
  484. if c.smClient != nil {
  485. err = c.smClient.Close()
  486. }
  487. if c.workloadIdentity != nil {
  488. err = c.workloadIdentity.Close()
  489. }
  490. useMu.Unlock()
  491. if err != nil {
  492. return fmt.Errorf(errClientClose, err)
  493. }
  494. return nil
  495. }
  496. func (c *Client) Validate() (esv1beta1.ValidationResult, error) {
  497. if c.storeKind == esv1beta1.ClusterSecretStoreKind && isReferentSpec(c.store) {
  498. return esv1beta1.ValidationResultUnknown, nil
  499. }
  500. return esv1beta1.ValidationResultReady, nil
  501. }
  502. func getDataByProperty(data []byte, property string) gjson.Result {
  503. var payload string
  504. if data != nil {
  505. payload = string(data)
  506. }
  507. idx := strings.Index(property, ".")
  508. refProperty := property
  509. if idx > 0 {
  510. refProperty = strings.ReplaceAll(refProperty, ".", "\\.")
  511. val := gjson.Get(payload, refProperty)
  512. if val.Exists() {
  513. return val
  514. }
  515. }
  516. return gjson.Get(payload, property)
  517. }