patch.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. package v2
  2. import (
  3. "context"
  4. "fmt"
  5. "strings"
  6. jsonpatch "github.com/evanphx/json-patch/v5"
  7. "github.com/porter-dev/api-contracts/generated/go/helpers"
  8. porterv1 "github.com/porter-dev/api-contracts/generated/go/porter/v1"
  9. "github.com/porter-dev/porter/internal/telemetry"
  10. )
  11. // PatchApp applies a set of JSON patch operations to an app proto and returns the modified proto
  12. func PatchApp(ctx context.Context, app *porterv1.PorterApp, ops []PatchOperation) (*porterv1.PorterApp, error) {
  13. ctx, span := telemetry.NewSpan(ctx, "v2-patch-app")
  14. defer span.End()
  15. var patchedApp *porterv1.PorterApp
  16. if app == nil {
  17. return patchedApp, telemetry.Error(ctx, span, nil, "no app provided")
  18. }
  19. by, err := helpers.MarshalContractObject(ctx, app)
  20. if err != nil {
  21. return patchedApp, telemetry.Error(ctx, span, err, "failed to marshal app")
  22. }
  23. var opStrs []string
  24. for _, op := range ops {
  25. opAsJSON, err := op.String()
  26. if err != nil {
  27. return patchedApp, telemetry.Error(ctx, span, err, "failed to convert patch operation to string")
  28. }
  29. opStrs = append(opStrs, fmt.Sprintf("\t%s", opAsJSON))
  30. }
  31. patchJson := fmt.Sprintf("[\n%s\n]", strings.Join(opStrs, ",\n"))
  32. telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "patch-json", Value: patchJson})
  33. patch, err := jsonpatch.DecodePatch([]byte(patchJson))
  34. if err != nil {
  35. return patchedApp, telemetry.Error(ctx, span, err, "failed to decode patch")
  36. }
  37. modified, err := patch.ApplyWithOptions(by, &jsonpatch.ApplyOptions{
  38. EnsurePathExistsOnAdd: true,
  39. })
  40. if err != nil {
  41. return patchedApp, telemetry.Error(ctx, span, err, "failed to apply patch")
  42. }
  43. patchedApp = &porterv1.PorterApp{}
  44. err = helpers.UnmarshalContractObject(modified, patchedApp)
  45. if err != nil {
  46. return patchedApp, telemetry.Error(ctx, span, err, "failed to unmarshal patched app")
  47. }
  48. return patchedApp, nil
  49. }
  50. // PatchOperationsFromFlagValuesInput is the input for PatchOperationsFromFlagValues
  51. type PatchOperationsFromFlagValuesInput struct {
  52. EnvGroups []string
  53. BuildMethod string
  54. Dockerfile string
  55. Builder string
  56. Buildpacks []string
  57. BuildContext string
  58. ImageRepository string
  59. ImageTag string
  60. }
  61. // PatchOperationsFromFlagValues converts the flag values into a list of patch operations
  62. func PatchOperationsFromFlagValues(inp PatchOperationsFromFlagValuesInput) []PatchOperation {
  63. var ops []PatchOperation
  64. var flags []ApplyFlag
  65. if inp.EnvGroups != nil {
  66. flags = append(flags, AttachEnvGroupsFlag{
  67. EnvGroups: inp.EnvGroups,
  68. })
  69. }
  70. if inp.BuildMethod != "" {
  71. flags = append(flags, SetBuildMethod{
  72. Method: inp.BuildMethod,
  73. })
  74. }
  75. if inp.Dockerfile != "" {
  76. flags = append(flags, SetBuildDockerfile{
  77. Dockerfile: inp.Dockerfile,
  78. })
  79. }
  80. if inp.Builder != "" {
  81. flags = append(flags, SetBuilder{
  82. Builder: inp.Builder,
  83. })
  84. }
  85. if inp.Buildpacks != nil {
  86. flags = append(flags, AttachBuildpacks{
  87. Buildpacks: inp.Buildpacks,
  88. })
  89. }
  90. if inp.BuildContext != "" {
  91. flags = append(flags, SetBuildContext{
  92. Context: inp.BuildContext,
  93. })
  94. }
  95. if inp.ImageRepository != "" {
  96. flags = append(flags, SetImageRepo{
  97. Repo: inp.ImageRepository,
  98. })
  99. }
  100. if inp.ImageTag != "" {
  101. flags = append(flags, SetImageTag{
  102. Tag: inp.ImageTag,
  103. })
  104. }
  105. for _, flag := range flags {
  106. ops = append(ops, flag.AsPatchOperations()...)
  107. }
  108. return ops
  109. }