|
6 | 6 | "fmt" |
7 | 7 |
|
8 | 8 | "github.com/google/uuid" |
| 9 | + "github.com/launchdarkly/go-sdk-common/v3/ldcontext" |
9 | 10 |
|
10 | 11 | "github.com/e2b-dev/infra/packages/orchestrator/pkg/sandbox/build" |
11 | 12 | "github.com/e2b-dev/infra/packages/shared/pkg/featureflags" |
@@ -36,14 +37,23 @@ func NewUpload( |
36 | 37 | useCase string, |
37 | 38 | objectMetadata storage.ObjectMetadata, |
38 | 39 | ) (*Upload, error) { |
| 40 | + mem, err := resolveCompressConfig(ctx, cfg, ff, storage.MemfileName, snap.MemfileDiffHeader.Metadata.BlockSize, useCase) |
| 41 | + if err != nil { |
| 42 | + return nil, fmt.Errorf("resolve memfile compress config: %w", err) |
| 43 | + } |
| 44 | + root, err := resolveCompressConfig(ctx, cfg, ff, storage.RootfsName, snap.RootfsDiffHeader.Metadata.BlockSize, useCase) |
| 45 | + if err != nil { |
| 46 | + return nil, fmt.Errorf("resolve rootfs compress config: %w", err) |
| 47 | + } |
| 48 | + |
39 | 49 | u := &Upload{ |
40 | 50 | buildID: snap.BuildID, |
41 | 51 | snap: snap, |
42 | 52 | paths: storage.Paths{BuildID: snap.BuildID.String()}, |
43 | 53 | uploads: uploads, |
44 | 54 | store: store, |
45 | | - mem: storage.ResolveCompressConfig(ctx, cfg, ff, storage.MemfileName, useCase), |
46 | | - root: storage.ResolveCompressConfig(ctx, cfg, ff, storage.RootfsName, useCase), |
| 55 | + mem: mem, |
| 56 | + root: root, |
47 | 57 | objectMetadata: objectMetadata, |
48 | 58 | } |
49 | 59 |
|
@@ -97,3 +107,79 @@ func (u *Upload) publish(ctx context.Context, t build.DiffType, h *headers.Heade |
97 | 107 |
|
98 | 108 | return nil |
99 | 109 | } |
| 110 | + |
| 111 | +// resolveCompressConfig returns the effective compression config for a given |
| 112 | +// file type and use case. Feature flags override the base config when active. |
| 113 | +// Returns zero-value CompressConfig when compression is disabled. |
| 114 | +// |
| 115 | +// fileType and useCase are added to the LD evaluation context so that |
| 116 | +// LaunchDarkly targeting rules can differentiate (e.g. compress memfile |
| 117 | +// but not rootfs, or compress builds but not pauses). blockSize is the |
| 118 | +// in-VM read granularity for this fileType (from the diff header) and |
| 119 | +// constrains the legal frame sizes — see validateCompressConfig. |
| 120 | +// |
| 121 | +// The resolved config is validated; an invalid env or LD-derived config |
| 122 | +// surfaces as an error so the upload fails fast rather than streaming with |
| 123 | +// a misconfigured frame size. |
| 124 | +func resolveCompressConfig(ctx context.Context, base storage.CompressConfig, ff *featureflags.Client, fileType string, blockSize uint64, useCase string) (storage.CompressConfig, error) { |
| 125 | + resolved := base |
| 126 | + |
| 127 | + if ff != nil { |
| 128 | + var extra []ldcontext.Context |
| 129 | + if fileType != "" { |
| 130 | + extra = append(extra, featureflags.CompressFileTypeContext(fileType)) |
| 131 | + } |
| 132 | + if useCase != "" { |
| 133 | + extra = append(extra, featureflags.CompressUseCaseContext(useCase)) |
| 134 | + } |
| 135 | + ctx = featureflags.AddToContext(ctx, extra...) |
| 136 | + |
| 137 | + v := ff.JSONFlag(ctx, featureflags.CompressConfigFlag).AsValueMap() |
| 138 | + |
| 139 | + if v.Get("compressBuilds").BoolValue() { |
| 140 | + ct := v.Get("compressionType").StringValue() |
| 141 | + ldCfg := storage.CompressConfig{ |
| 142 | + Enabled: true, |
| 143 | + Type: ct, |
| 144 | + Level: v.Get("compressionLevel").IntValue(), |
| 145 | + FrameSizeKB: v.Get("frameSizeKB").IntValue(), |
| 146 | + MinPartSizeMB: v.Get("minPartSizeMB").IntValue(), |
| 147 | + FrameEncodeWorkers: v.Get("frameEncodeWorkers").IntValue(), |
| 148 | + EncoderConcurrency: v.Get("encoderConcurrency").IntValue(), |
| 149 | + } |
| 150 | + if ldCfg.CompressionType() != storage.CompressionNone { |
| 151 | + resolved = ldCfg |
| 152 | + } |
| 153 | + } |
| 154 | + } |
| 155 | + |
| 156 | + if !resolved.IsCompressionEnabled() { |
| 157 | + return storage.CompressConfig{}, nil |
| 158 | + } |
| 159 | + |
| 160 | + if err := validateCompressConfig(resolved, blockSize); err != nil { |
| 161 | + return storage.CompressConfig{}, err |
| 162 | + } |
| 163 | + |
| 164 | + return resolved, nil |
| 165 | +} |
| 166 | + |
| 167 | +// validateCompressConfig checks that the resolved config is internally |
| 168 | +// consistent for the given block size. Frame size must be a positive multiple |
| 169 | +// of blockSize so that every block-sized read served by the chunker lies |
| 170 | +// inside one frame — otherwise Chunker.fetch fetches only the start frame and |
| 171 | +// cache.sliceDirect returns uninitialized mmap bytes for the tail. |
| 172 | +func validateCompressConfig(c storage.CompressConfig, blockSize uint64) error { |
| 173 | + fs := c.FrameSize() |
| 174 | + if fs <= 0 { |
| 175 | + return fmt.Errorf("frame size must be positive, got %d KB", c.FrameSizeKB) |
| 176 | + } |
| 177 | + if blockSize == 0 { |
| 178 | + return errors.New("block size must be positive") |
| 179 | + } |
| 180 | + if uint64(fs)%blockSize != 0 { |
| 181 | + return fmt.Errorf("frame size (%d) must be a multiple of block size (%d)", fs, blockSize) |
| 182 | + } |
| 183 | + |
| 184 | + return nil |
| 185 | +} |
0 commit comments