Skip to content

Commit f84ef6e

Browse files
refactor: parse state directly instead of using tf show (#391)
also: - enable pprof - add tracing - remove readonly logic (was no longer being used, as we lock every time)
1 parent 9d615ed commit f84ef6e

File tree

19 files changed

+182
-188
lines changed

19 files changed

+182
-188
lines changed

api/deploy/deploy.go

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"github.com/multycloud/multy/api/errors"
66
"github.com/multycloud/multy/api/util"
7+
"github.com/multycloud/multy/db"
78
"github.com/multycloud/multy/flags"
89
"github.com/multycloud/multy/resources"
910
"github.com/multycloud/multy/resources/output"
@@ -40,14 +41,14 @@ func NewDeploymentExecutor() DeploymentExecutor {
4041
return DeploymentExecutor{TfCmd: terraformCmd{}}
4142
}
4243

43-
func (d DeploymentExecutor) Deploy(ctx context.Context, c *resources.MultyConfig, prev resources.Resource, curr resources.Resource) (state *output.TfState, rollbackFn func(), err error) {
44-
tmpDir := GetTempDirForUser(false, c.GetUserId())
45-
encoded, err := d.EncodeAndStoreTfFile(ctx, c, prev, curr, false)
44+
func (d DeploymentExecutor) Deploy(ctx context.Context, c *resources.MultyConfig, prev resources.Resource, curr resources.Resource) (rollbackFn func(), err error) {
45+
tmpDir := GetTempDirForUser(c.GetUserId())
46+
encoded, err := d.EncodeAndStoreTfFile(ctx, c, prev, curr)
4647
if err != nil {
4748
return
4849
}
4950

50-
err = d.MaybeInit(ctx, c.GetUserId(), false)
51+
err = d.MaybeInit(ctx, c.GetUserId())
5152
if err != nil {
5253
return
5354
}
@@ -67,7 +68,7 @@ func (d DeploymentExecutor) Deploy(ctx context.Context, c *resources.MultyConfig
6768
log.Printf("[ERROR] Rollback unsuccessful: %s\n", err2)
6869
return
6970
}
70-
_, err2 = d.EncodeAndStoreTfFile(ctx, originalC, curr, prev, false)
71+
_, err2 = d.EncodeAndStoreTfFile(ctx, originalC, curr, prev)
7172
if err2 != nil {
7273
log.Printf("[ERROR] Rollback unsuccessful: %s\n", err2)
7374
return
@@ -92,15 +93,10 @@ func (d DeploymentExecutor) Deploy(ctx context.Context, c *resources.MultyConfig
9293
return
9394
}
9495

95-
state, err = d.GetState(ctx, c.GetUserId(), false)
96-
if err != nil {
97-
return state, rollbackFn, errors.InternalServerErrorWithMessage("error parsing state", err)
98-
}
99-
10096
return
10197
}
10298

103-
func (d DeploymentExecutor) EncodeAndStoreTfFile(ctx context.Context, c *resources.MultyConfig, prev resources.Resource, curr resources.Resource, readonly bool) (EncodedResources, error) {
99+
func (d DeploymentExecutor) EncodeAndStoreTfFile(ctx context.Context, c *resources.MultyConfig, prev resources.Resource, curr resources.Resource) (EncodedResources, error) {
104100
credentials, err := util.ExtractCloudCredentials(ctx)
105101
if err != nil {
106102
return EncodedResources{}, err
@@ -118,7 +114,7 @@ func (d DeploymentExecutor) EncodeAndStoreTfFile(ctx context.Context, c *resourc
118114
// TODO: move this to a proper place
119115
hclOutput := tfBlock + encoded.HclString
120116

121-
tmpDir := GetTempDirForUser(readonly, c.GetUserId())
117+
tmpDir := GetTempDirForUser(c.GetUserId())
122118
err = os.MkdirAll(tmpDir, os.ModeDir|(os.ModePerm&0775))
123119
if err != nil {
124120
return EncodedResources{}, err
@@ -127,8 +123,8 @@ func (d DeploymentExecutor) EncodeAndStoreTfFile(ctx context.Context, c *resourc
127123
return encoded, err
128124
}
129125

130-
func (d DeploymentExecutor) MaybeInit(ctx context.Context, userId string, readonly bool) error {
131-
tmpDir := GetTempDirForUser(readonly, userId)
126+
func (d DeploymentExecutor) MaybeInit(ctx context.Context, userId string) error {
127+
tmpDir := GetTempDirForUser(userId)
132128
_, err := os.Stat(filepath.Join(tmpDir, tfDir))
133129
if os.IsNotExist(err) {
134130
start := time.Now()
@@ -151,22 +147,26 @@ func (d DeploymentExecutor) MaybeInit(ctx context.Context, userId string, readon
151147
return nil
152148
}
153149

154-
func (d DeploymentExecutor) GetState(ctx context.Context, userId string, readonly bool) (*output.TfState, error) {
155-
tmpDir := GetTempDirForUser(readonly, userId)
156-
return d.TfCmd.GetState(ctx, tmpDir)
150+
func (d DeploymentExecutor) GetState(ctx context.Context, userId string, client db.TfStateReader) (*output.TfState, error) {
151+
return d.TfCmd.GetState(ctx, userId, client)
157152
}
158153

159154
func (d DeploymentExecutor) RefreshState(ctx context.Context, userId string, c *resources.MultyConfig) error {
160-
_, err := d.EncodeAndStoreTfFile(ctx, c, nil, nil, true)
155+
_, err := d.EncodeAndStoreTfFile(ctx, c, nil, nil)
161156
if err != nil {
162157
return err
163158
}
164159

165-
err = d.MaybeInit(ctx, userId, true)
160+
err = d.MaybeInit(ctx, userId)
166161
if err != nil {
167162
return err
168163
}
169164

165+
start := time.Now()
166+
defer func() {
167+
log.Printf("[DEBUG] refresh finished in %s", time.Since(start))
168+
}()
169+
170170
return d.refresh(ctx, userId)
171171
}
172172

@@ -176,20 +176,16 @@ func (d DeploymentExecutor) refresh(ctx context.Context, userId string) error {
176176
log.Printf("[DEBUG] refresh finished in %s", time.Since(start))
177177
}()
178178

179-
tmpDir := GetTempDirForUser(true, userId)
179+
tmpDir := GetTempDirForUser(userId)
180180
return d.TfCmd.Refresh(ctx, tmpDir)
181181
}
182182

183-
func GetTempDirForUser(readonly bool, userId string) string {
183+
func GetTempDirForUser(userId string) string {
184184
tmpDir := filepath.Join(os.TempDir(), "multy", userId)
185185

186186
if flags.Environment == flags.Local {
187187
tmpDir = filepath.Join(tmpDir, "local")
188188
}
189189

190-
if readonly {
191-
tmpDir = filepath.Join(tmpDir, "readonly")
192-
}
193-
194190
return tmpDir
195191
}

api/deploy/provider_versions.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package deploy
22

33
import (
44
"fmt"
5+
"github.com/multycloud/multy/db"
56
"github.com/multycloud/multy/flags"
67
"os"
78
"path"
@@ -14,7 +15,6 @@ const (
1415
randomProviderVersion = "3.1.3"
1516
localProviderVersion = "2.2.2"
1617
tfStateRegion = "eu-west-2"
17-
tfState = "terraform.tfstate"
1818
)
1919

2020
func GetTerraformBlock(userId string) (string, error) {
@@ -29,7 +29,7 @@ func GetTerraformBlock(userId string) (string, error) {
2929
}
3030

3131
func getLocalStateBlock(userId string) string {
32-
p := path.Join(os.TempDir(), "multy", userId, "local", tfState)
32+
p := path.Join(os.TempDir(), "multy", userId, "local", db.TfState)
3333
return fmt.Sprintf(
3434
`backend "local" {
3535
path = "%s"
@@ -41,7 +41,7 @@ func getRemoteStateBlock(userId, userStorageName string) string {
4141
key = "%s/%s"
4242
region = "%s"
4343
profile = "multy"
44-
}`, userStorageName, userId, tfState, tfStateRegion)
44+
}`, userStorageName, userId, db.TfState, tfStateRegion)
4545
}
4646

4747
func getTerraformBlock(providerBlock string) (string, error) {

api/deploy/terraform_command.go

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,21 @@ import (
66
"encoding/json"
77
"fmt"
88
"github.com/multycloud/multy/api/errors"
9+
"github.com/multycloud/multy/db"
910
"github.com/multycloud/multy/flags"
1011
"github.com/multycloud/multy/resources/output"
1112
"io"
1213
"log"
1314
"os"
1415
"os/exec"
16+
"runtime/trace"
1517
)
1618

1719
type TerraformCommand interface {
1820
Init(ctx context.Context, dir string) error
1921
Apply(ctx context.Context, dir string, resources []string) error
2022
Refresh(ctx context.Context, dir string) error
21-
GetState(ctx context.Context, dir string) (*output.TfState, error)
23+
GetState(ctx context.Context, userId string, dir db.TfStateReader) (*output.TfState, error)
2224
}
2325

2426
type terraformCmd struct {
@@ -38,13 +40,18 @@ func (c terraformCmd) Apply(ctx context.Context, dir string, resources []string)
3840
if len(resources) == 0 {
3941
return nil
4042
}
43+
44+
region := trace.StartRegion(ctx, "tf apply")
45+
defer region.End()
46+
4147
var targetArgs []string
4248

43-
log.Println("[INFO] Running apply for targets:")
49+
idsToPrint := ""
4450
for _, id := range resources {
45-
log.Printf("[INFO] %s", id)
51+
idsToPrint += id + ", "
4652
targetArgs = append(targetArgs, "-target="+id)
4753
}
54+
log.Printf("[INFO] Running apply for targets: %s", idsToPrint)
4855
cmd := exec.CommandContext(ctx, "terraform", append([]string{"-chdir=" + dir, "apply", "-refresh=false", "-auto-approve", "--json"}, targetArgs...)...)
4956
if flags.DryRun {
5057
cmd = exec.CommandContext(ctx, "terraform", append([]string{"-chdir=" + dir, "plan", "-refresh=false", "--json"}, targetArgs...)...)
@@ -68,6 +75,9 @@ func (c terraformCmd) Apply(ctx context.Context, dir string, resources []string)
6875
}
6976

7077
func (c terraformCmd) Init(ctx context.Context, dir string) error {
78+
region := trace.StartRegion(ctx, "tf init")
79+
defer region.End()
80+
7181
cmd := exec.CommandContext(ctx, "terraform", "-chdir="+dir, "init", "-reconfigure", "-lock-timeout", "1m")
7282
cmd.Stdout = os.Stdout
7383
cmd.Stderr = os.Stderr
@@ -79,6 +89,9 @@ func (c terraformCmd) Init(ctx context.Context, dir string) error {
7989
}
8090

8191
func (c terraformCmd) Refresh(ctx context.Context, dir string) error {
92+
region := trace.StartRegion(ctx, "tf refresh")
93+
defer region.End()
94+
8295
outputJson := new(bytes.Buffer)
8396
cmd := exec.CommandContext(ctx, "terraform", "-chdir="+dir, "refresh", "-json")
8497
cmd.Stdout = outputJson
@@ -97,18 +110,18 @@ func (c terraformCmd) Refresh(ctx context.Context, dir string) error {
97110
return err
98111
}
99112

100-
func (c terraformCmd) GetState(ctx context.Context, dir string) (*output.TfState, error) {
101-
state := output.TfState{}
102-
stateJson := new(bytes.Buffer)
103-
cmd := exec.CommandContext(ctx, "terraform", "-chdir="+dir, "show", "-json")
104-
cmd.Stdout = stateJson
105-
cmd.Stderr = os.Stderr
106-
err := cmd.Run()
113+
func (c terraformCmd) GetState(ctx context.Context, userId string, client db.TfStateReader) (*output.TfState, error) {
114+
region := trace.StartRegion(ctx, "tf show")
115+
defer region.End()
116+
117+
terraformState, err := client.LoadTerraformState(ctx, userId)
107118
if err != nil {
108-
return &state, err
119+
return nil, err
109120
}
110121

111-
err = json.Unmarshal(stateJson.Bytes(), &state)
122+
state := output.TfState{}
123+
124+
err = json.Unmarshal([]byte(terraformState), &state)
112125
if err != nil {
113126
return nil, err
114127
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package interceptors
2+
3+
import (
4+
"context"
5+
"google.golang.org/grpc"
6+
"runtime/trace"
7+
)
8+
9+
func TraceUnaryInterceptor() grpc.UnaryServerInterceptor {
10+
return TraceInterceptor
11+
}
12+
13+
func TraceInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
14+
ctx, task := trace.NewTask(ctx, "request")
15+
trace.Log(ctx, "method", info.FullMethod)
16+
defer task.End()
17+
// Invoke the original method call
18+
return handler(ctx, req)
19+
}

api/server.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ import (
66
aws_client "github.com/multycloud/multy/api/aws"
77
"github.com/multycloud/multy/api/deploy"
88
"github.com/multycloud/multy/api/errors"
9+
"github.com/multycloud/multy/api/interceptors"
910
"github.com/multycloud/multy/api/proto"
1011
"github.com/multycloud/multy/api/proto/commonpb"
1112
"github.com/multycloud/multy/api/proto/resourcespb"
1213
"github.com/multycloud/multy/api/service_context"
1314
"github.com/multycloud/multy/api/services"
1415
"github.com/multycloud/multy/api/util"
1516
"github.com/multycloud/multy/db"
16-
"github.com/multycloud/multy/flags"
1717
"github.com/multycloud/multy/resources"
1818
"github.com/multycloud/multy/resources/types/metadata"
1919
"google.golang.org/grpc"
@@ -57,9 +57,9 @@ func RunServer(ctx context.Context, port int) {
5757
}
5858

5959
var s *grpc.Server
60-
if flags.Environment == flags.Local {
61-
log.Println("[INFO] running in local mode")
62-
s = grpc.NewServer()
60+
if port != 443 {
61+
log.Println("[INFO] Running in HTTP mode, port is not 443")
62+
s = grpc.NewServer(grpc.UnaryInterceptor(interceptors.TraceUnaryInterceptor()))
6363
} else {
6464
endpoint, exists := os.LookupEnv("MULTY_API_ENDPOINT")
6565
if !exists {
@@ -72,7 +72,7 @@ func RunServer(ctx context.Context, port int) {
7272
if err != nil {
7373
log.Fatalf("unable to read certificate (%s)", err.Error())
7474
}
75-
s = grpc.NewServer(grpc.Creds(creds))
75+
s = grpc.NewServer(grpc.Creds(creds), grpc.UnaryInterceptor(interceptors.TraceUnaryInterceptor()))
7676
}
7777

7878
go func() {
@@ -379,7 +379,7 @@ func (s *Server) refresh(ctx context.Context, _ *commonpb.Empty) (*commonpb.Empt
379379
}
380380
defer s.Database.UnlockConfig(ctx, lock)
381381

382-
c, err := s.Database.LoadUserConfig(userId, lock)
382+
c, err := s.Database.LoadUserConfig(ctx, userId, lock)
383383
if err != nil {
384384
return nil, err
385385
}
@@ -393,7 +393,7 @@ func (s *Server) refresh(ctx context.Context, _ *commonpb.Empty) (*commonpb.Empt
393393
return nil, err
394394
}
395395

396-
err = s.Database.StoreUserConfig(c, lock)
396+
err = s.Database.StoreUserConfig(ctx, c, lock)
397397
if err != nil {
398398
return nil, err
399399
}
@@ -422,7 +422,7 @@ func (s *Server) list(ctx context.Context, _ *commonpb.Empty) (*commonpb.ListRes
422422
return nil, err
423423
}
424424

425-
c, err := s.Database.LoadUserConfig(userId, nil)
425+
c, err := s.Database.LoadUserConfig(ctx, userId, nil)
426426
if err != nil {
427427
return nil, err
428428
}
@@ -467,7 +467,7 @@ func (s *Server) deleteResource(ctx context.Context, req *proto.DeleteResourceRe
467467
return nil, err
468468
}
469469
defer s.Database.UnlockConfig(ctx, lock)
470-
c, err := s.Database.LoadUserConfig(userId, lock)
470+
c, err := s.Database.LoadUserConfig(ctx, userId, lock)
471471
if err != nil {
472472
return nil, err
473473
}
@@ -477,7 +477,7 @@ func (s *Server) deleteResource(ctx context.Context, req *proto.DeleteResourceRe
477477
return nil, err
478478
}
479479

480-
err = s.Database.StoreUserConfig(c, lock)
480+
err = s.Database.StoreUserConfig(ctx, c, lock)
481481
if err != nil {
482482
return nil, err
483483
}

0 commit comments

Comments
 (0)