Skip to content

Commit 39872c1

Browse files
Merge pull request #27059 from Honny1/build-local-api
Add local build API for direct filesystem builds on MacOS and Windows
2 parents 7ebc20b + 1656c90 commit 39872c1

File tree

11 files changed

+844
-47
lines changed

11 files changed

+844
-47
lines changed

internal/localapi/utils.go

Lines changed: 143 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"strings"
1414

1515
"github.com/containers/podman/v5/pkg/bindings"
16+
"github.com/containers/podman/v5/pkg/domain/entities"
1617
"github.com/containers/podman/v5/pkg/machine/define"
1718
"github.com/containers/podman/v5/pkg/machine/env"
1819
"github.com/containers/podman/v5/pkg/machine/provider"
@@ -25,44 +26,46 @@ import (
2526
// FindMachineByPort finds a running machine that matches the given connection port.
2627
// It returns the machine configuration and provider, or an error if not found.
2728
func FindMachineByPort(connectionURI string, parsedConnection *url.URL) (*vmconfigs.MachineConfig, vmconfigs.VMProvider, error) {
28-
machineProvider, err := provider.Get()
29-
if err != nil {
30-
return nil, nil, fmt.Errorf("getting machine provider: %w", err)
31-
}
32-
33-
dirs, err := env.GetMachineDirs(machineProvider.VMType())
34-
if err != nil {
35-
return nil, nil, err
36-
}
37-
38-
machineList, err := vmconfigs.LoadMachinesInDir(dirs)
39-
if err != nil {
40-
return nil, nil, fmt.Errorf("listing machines: %w", err)
41-
}
42-
43-
// Now we know that the connection points to a machine and we
44-
// can find the machine by looking for the one with the
45-
// matching port.
46-
connectionPort, err := strconv.Atoi(parsedConnection.Port())
47-
if err != nil {
48-
return nil, nil, fmt.Errorf("parsing connection port: %w", err)
49-
}
50-
51-
for _, mc := range machineList {
52-
if connectionPort != mc.SSH.Port {
29+
for _, machineProvider := range provider.GetAll() {
30+
logrus.Debugf("Checking provider: %s", machineProvider.VMType())
31+
dirs, err := env.GetMachineDirs(machineProvider.VMType())
32+
if err != nil {
33+
logrus.Debugf("Failed to get machine dirs for provider %s: %v", machineProvider.VMType(), err)
5334
continue
5435
}
5536

56-
state, err := machineProvider.State(mc, false)
37+
machineList, err := vmconfigs.LoadMachinesInDir(dirs)
5738
if err != nil {
58-
return nil, nil, err
39+
logrus.Debugf("Failed to list machines: %v", err)
40+
continue
5941
}
6042

61-
if state != define.Running {
62-
return nil, nil, fmt.Errorf("machine %s is not running but in state %s", mc.Name, state)
43+
// Now we know that the connection points to a machine and we
44+
// can find the machine by looking for the one with the
45+
// matching port.
46+
connectionPort, err := strconv.Atoi(parsedConnection.Port())
47+
if err != nil {
48+
logrus.Debugf("Failed to parse connection port: %v", err)
49+
continue
6350
}
6451

65-
return mc, machineProvider, nil
52+
for _, mc := range machineList {
53+
if connectionPort != mc.SSH.Port {
54+
continue
55+
}
56+
57+
state, err := machineProvider.State(mc, false)
58+
if err != nil {
59+
logrus.Debugf("Failed to get machine state for %s: %v", mc.Name, err)
60+
continue
61+
}
62+
63+
if state != define.Running {
64+
return nil, nil, fmt.Errorf("machine %s is not running but in state %s", mc.Name, state)
65+
}
66+
67+
return mc, machineProvider, nil
68+
}
6669
}
6770

6871
return nil, nil, fmt.Errorf("could not find a matching machine for connection %q", connectionURI)
@@ -154,3 +157,113 @@ func CheckPathOnRunningMachine(ctx context.Context, path string) (*LocalAPIMap,
154157

155158
return isPathAvailableOnMachine(mounts, vmType, path)
156159
}
160+
161+
// CheckIfImageBuildPathsOnRunningMachine checks if the build context directory and all specified
162+
// Containerfiles are available on the running machine. If they are, it translates their paths
163+
// to the corresponding remote paths and returns them along with a flag indicating success.
164+
func CheckIfImageBuildPathsOnRunningMachine(ctx context.Context, containerFiles []string, options entities.BuildOptions) ([]string, entities.BuildOptions, bool) {
165+
if machineMode := bindings.GetMachineMode(ctx); !machineMode {
166+
logrus.Debug("Machine mode is not enabled, skipping machine check")
167+
return nil, options, false
168+
}
169+
170+
conn, err := bindings.GetClient(ctx)
171+
if err != nil {
172+
logrus.Debugf("Failed to get client connection: %v", err)
173+
return nil, options, false
174+
}
175+
176+
mounts, vmType, err := getMachineMountsAndVMType(conn.URI.String(), conn.URI)
177+
if err != nil {
178+
logrus.Debugf("Failed to get machine mounts: %v", err)
179+
return nil, options, false
180+
}
181+
182+
// Context directory
183+
if err := fileutils.Lexists(options.ContextDirectory); errors.Is(err, fs.ErrNotExist) {
184+
logrus.Debugf("Path %s does not exist locally, skipping machine check", options.ContextDirectory)
185+
return nil, options, false
186+
}
187+
mapping, found := isPathAvailableOnMachine(mounts, vmType, options.ContextDirectory)
188+
if !found {
189+
logrus.Debugf("Path %s is not available on the running machine", options.ContextDirectory)
190+
return nil, options, false
191+
}
192+
options.ContextDirectory = mapping.RemotePath
193+
194+
// Containerfiles
195+
translatedContainerFiles := []string{}
196+
for _, containerFile := range containerFiles {
197+
if strings.HasPrefix(containerFile, "http://") || strings.HasPrefix(containerFile, "https://") {
198+
translatedContainerFiles = append(translatedContainerFiles, containerFile)
199+
continue
200+
}
201+
202+
// If Containerfile does not exist, assume it is in context directory
203+
if err := fileutils.Lexists(containerFile); err != nil {
204+
if !errors.Is(err, fs.ErrNotExist) {
205+
logrus.Fatalf("Failed to check if containerfile %s exists: %v", containerFile, err)
206+
return nil, options, false
207+
}
208+
continue
209+
}
210+
211+
mapping, found := isPathAvailableOnMachine(mounts, vmType, containerFile)
212+
if !found {
213+
logrus.Debugf("Path %s is not available on the running machine", containerFile)
214+
return nil, options, false
215+
}
216+
translatedContainerFiles = append(translatedContainerFiles, mapping.RemotePath)
217+
}
218+
219+
// Additional build contexts
220+
for _, context := range options.AdditionalBuildContexts {
221+
switch {
222+
case context.IsImage, context.IsURL:
223+
continue
224+
default:
225+
if err := fileutils.Lexists(context.Value); errors.Is(err, fs.ErrNotExist) {
226+
logrus.Debugf("Path %s does not exist locally, skipping machine check", context.Value)
227+
return nil, options, false
228+
}
229+
mapping, found := isPathAvailableOnMachine(mounts, vmType, context.Value)
230+
if !found {
231+
logrus.Debugf("Path %s is not available on the running machine", context.Value)
232+
return nil, options, false
233+
}
234+
context.Value = mapping.RemotePath
235+
}
236+
}
237+
return translatedContainerFiles, options, true
238+
}
239+
240+
// IsHyperVProvider checks if the current machine provider is Hyper-V.
241+
// It returns true if the provider is Hyper-V, false otherwise, or an error if the check fails.
242+
func IsHyperVProvider(ctx context.Context) (bool, error) {
243+
conn, err := bindings.GetClient(ctx)
244+
if err != nil {
245+
logrus.Debugf("Failed to get client connection: %v", err)
246+
return false, err
247+
}
248+
249+
_, vmProvider, err := FindMachineByPort(conn.URI.String(), conn.URI)
250+
if err != nil {
251+
logrus.Debugf("Failed to get machine hypervisor type: %v", err)
252+
return false, err
253+
}
254+
255+
return vmProvider.VMType() == define.HyperVVirt, nil
256+
}
257+
258+
// ValidatePathForLocalAPI checks if the provided path satisfies requirements for local API usage.
259+
// It returns an error if the path is not absolute or does not exist on the filesystem.
260+
func ValidatePathForLocalAPI(path string) error {
261+
if !filepath.IsAbs(path) {
262+
return fmt.Errorf("path %q is not absolute", path)
263+
}
264+
265+
if err := fileutils.Exists(path); err != nil {
266+
return err
267+
}
268+
return nil
269+
}

internal/localapi/utils_unsupported.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,26 @@ package localapi
55
import (
66
"context"
77

8+
"github.com/containers/podman/v5/pkg/domain/entities"
89
"github.com/sirupsen/logrus"
910
)
1011

1112
func CheckPathOnRunningMachine(ctx context.Context, path string) (*LocalAPIMap, bool) {
1213
logrus.Debug("CheckPathOnRunningMachine is not supported")
1314
return nil, false
1415
}
16+
17+
func CheckIfImageBuildPathsOnRunningMachine(ctx context.Context, containerFiles []string, options entities.BuildOptions) ([]string, entities.BuildOptions, bool) {
18+
logrus.Debug("CheckIfImageBuildPathsOnRunningMachine is not supported")
19+
return nil, options, false
20+
}
21+
22+
func IsHyperVProvider(ctx context.Context) (bool, error) {
23+
logrus.Debug("IsHyperVProvider is not supported")
24+
return false, nil
25+
}
26+
27+
func ValidatePathForLocalAPI(path string) error {
28+
logrus.Debug("ValidatePathForLocalAPI is not supported")
29+
return nil
30+
}

0 commit comments

Comments
 (0)