@@ -135,6 +135,40 @@ type BuildContext struct {
135
135
IgnoreFile string
136
136
}
137
137
138
+ func (b * BuildContext ) validatePaths () error {
139
+ if err := validateLocalPath (b .ContextDirectory ); err != nil {
140
+ return err
141
+ }
142
+
143
+ for _ , containerfile := range b .ContainerFiles {
144
+ if err := validateLocalPath (containerfile ); err != nil {
145
+ return err
146
+ }
147
+ }
148
+
149
+ for _ , ctx := range b .AdditionalBuildContexts {
150
+ if ctx .IsURL || ctx .IsImage {
151
+ continue
152
+ }
153
+ if err := validateLocalPath (ctx .Value ); err != nil {
154
+ return err
155
+ }
156
+ }
157
+
158
+ return nil
159
+ }
160
+
161
+ func validateLocalPath (path string ) error {
162
+ if ! filepath .IsAbs (path ) {
163
+ return fmt .Errorf ("path %q is not absolute" , path )
164
+ }
165
+
166
+ if err := fileutils .Exists (path ); err != nil {
167
+ return err
168
+ }
169
+ return nil
170
+ }
171
+
138
172
// genSpaceErr wraps filesystem errors to provide more context for disk space issues.
139
173
func genSpaceErr (err error ) error {
140
174
if errors .Is (err , syscall .ENOSPC ) {
@@ -257,10 +291,17 @@ func processBuildContext(query url.Values, r *http.Request, buildContext *BuildC
257
291
258
292
for _ , containerfile := range m {
259
293
// Add path to containerfile iff it is not URL
260
- if ! strings .HasPrefix (containerfile , "http://" ) && ! strings .HasPrefix (containerfile , "https://" ) {
294
+ if strings .HasPrefix (containerfile , "http://" ) || strings .HasPrefix (containerfile , "https://" ) {
295
+ continue
296
+ }
297
+
298
+ if filepath .IsAbs (containerfile ) {
299
+ containerfile = filepath .Clean (filepath .FromSlash (containerfile ))
300
+ } else {
261
301
containerfile = filepath .Join (buildContext .ContextDirectory ,
262
302
filepath .Clean (filepath .FromSlash (containerfile )))
263
303
}
304
+
264
305
buildContext .ContainerFiles = append (buildContext .ContainerFiles , containerfile )
265
306
}
266
307
dockerFileSet = true
@@ -819,7 +860,104 @@ func executeBuild(runtime *libpod.Runtime, w http.ResponseWriter, r *http.Reques
819
860
}
820
861
}
821
862
863
+ // handleLocalBuildContexts processes build contexts for local builds using filesystem paths.
864
+ // It extracts the local context directory and processes additional build contexts.
865
+ func handleLocalBuildContexts (query url.Values , anchorDir string ) (* BuildContext , error ) {
866
+ localContextDir := query .Get ("localcontextdir" )
867
+ if localContextDir == "" {
868
+ return nil , fmt .Errorf ("localcontextdir cannot be empty" )
869
+ }
870
+
871
+ out := & BuildContext {
872
+ ContextDirectory : filepath .Clean (localContextDir ),
873
+ AdditionalBuildContexts : make (map [string ]* buildahDefine.AdditionalBuildContext ),
874
+ }
875
+
876
+ for _ , url := range query ["additionalbuildcontexts" ] {
877
+ name , value , found := strings .Cut (url , "=" )
878
+ if ! found {
879
+ return nil , fmt .Errorf ("invalid additional build context format: %q" , url )
880
+ }
881
+
882
+ logrus .Debugf ("name: %q, context: %q" , name , value )
883
+
884
+ switch {
885
+ case strings .HasPrefix (value , "url:" ):
886
+ value = strings .TrimPrefix (value , "url:" )
887
+ tempDir , subdir , err := buildahDefine .TempDirForURL (anchorDir , "buildah" , value )
888
+ if err != nil {
889
+ return nil , fmt .Errorf ("downloading URL %q: %w" , name , err )
890
+ }
891
+
892
+ contextPath := filepath .Join (tempDir , subdir )
893
+ out .AdditionalBuildContexts [name ] = & buildahDefine.AdditionalBuildContext {
894
+ IsURL : true ,
895
+ IsImage : false ,
896
+ Value : contextPath ,
897
+ DownloadedCache : contextPath ,
898
+ }
899
+ case strings .HasPrefix (value , "image:" ):
900
+ value = strings .TrimPrefix (value , "image:" )
901
+ out .AdditionalBuildContexts [name ] = & buildahDefine.AdditionalBuildContext {
902
+ IsURL : false ,
903
+ IsImage : true ,
904
+ Value : value ,
905
+ }
906
+ case strings .HasPrefix (value , "localpath:" ):
907
+ value = strings .TrimPrefix (value , "localpath:" )
908
+ fmt .Printf ("Using local path %q for additional build context %q\n " , value , name )
909
+ out .AdditionalBuildContexts [name ] = & buildahDefine.AdditionalBuildContext {
910
+ IsURL : false ,
911
+ IsImage : false ,
912
+ Value : filepath .Clean (value ),
913
+ }
914
+ }
915
+ }
916
+ return out , nil
917
+ }
918
+
919
+ // getLocalBuildContext processes build contexts from HTTP request to a BuildContext struct.
920
+ func getLocalBuildContext (r * http.Request , query url.Values , anchorDir string , multipart bool ) (* BuildContext , error ) {
921
+ // Handle build contexts (extract from tar/multipart)
922
+ buildContext , err := handleLocalBuildContexts (query , anchorDir )
923
+ if err != nil {
924
+ return nil , utils .GetInternalServerError (genSpaceErr (err ))
925
+ }
926
+
927
+ // Process build context and container files
928
+ buildContext , err = processBuildContext (query , r , buildContext , anchorDir )
929
+ if err != nil {
930
+ return nil , err
931
+ }
932
+
933
+ if err := buildContext .validatePaths (); err != nil {
934
+ if errors .Is (err , os .ErrNotExist ) {
935
+ return nil , utils .GetFileNotFoundError (err )
936
+ }
937
+ return nil , utils .GetGenericBadRequestError (err )
938
+ }
939
+
940
+ // Process dockerignore
941
+ _ , ignoreFile , err := util .ParseDockerignore (buildContext .ContainerFiles , buildContext .ContextDirectory )
942
+ if err != nil {
943
+ return nil , utils .GetInternalServerError (fmt .Errorf ("processing ignore file: %w" , err ))
944
+ }
945
+ buildContext .IgnoreFile = ignoreFile
946
+
947
+ return buildContext , nil
948
+ }
949
+
950
+ func LocalBuildImage (w http.ResponseWriter , r * http.Request ) {
951
+ buildImage (w , r , getLocalBuildContext )
952
+ }
953
+
822
954
func BuildImage (w http.ResponseWriter , r * http.Request ) {
955
+ buildImage (w , r , getBuildContext )
956
+ }
957
+
958
+ type getBuildContextFunc func (r * http.Request , query url.Values , anchorDir string , multipart bool ) (* BuildContext , error )
959
+
960
+ func buildImage (w http.ResponseWriter , r * http.Request , getBuildContextFunc getBuildContextFunc ) {
823
961
// Create temporary directory for build context
824
962
anchorDir , err := os .MkdirTemp (parse .GetTempDir (), "libpod_builder" )
825
963
if err != nil {
@@ -850,7 +988,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
850
988
}
851
989
queryValues := r .URL .Query ()
852
990
853
- buildContext , err := getBuildContext (r , queryValues , anchorDir , multipart )
991
+ buildContext , err := getBuildContextFunc (r , queryValues , anchorDir , multipart )
854
992
if err != nil {
855
993
utils .ProcessBuildError (w , err )
856
994
return
0 commit comments