@@ -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 ) {
@@ -188,7 +222,7 @@ func validateContentType(r *http.Request) (bool, error) {
188
222
logrus .Infof ("Received %s" , hdr [0 ])
189
223
multipart = true
190
224
default :
191
- if utils .IsLibpodRequest (r ) {
225
+ if utils .IsLibpodRequest (r ) && ! utils . IsLibpodLocalRequest ( r ) {
192
226
return false , utils .GetBadRequestError ("Content-Type" , hdr [0 ],
193
227
fmt .Errorf ("Content-Type: %s is not supported. Should be \" application/x-tar\" " , hdr [0 ]))
194
228
}
@@ -256,10 +290,14 @@ func processBuildContext(query url.Values, r *http.Request, buildContext *BuildC
256
290
}
257
291
258
292
for _ , containerfile := range m {
259
- // Add path to containerfile iff it is not URL
293
+ // Add path to containerfile if it is not URL
260
294
if ! strings .HasPrefix (containerfile , "http://" ) && ! strings .HasPrefix (containerfile , "https://" ) {
261
- containerfile = filepath .Join (buildContext .ContextDirectory ,
262
- filepath .Clean (filepath .FromSlash (containerfile )))
295
+ if filepath .IsAbs (containerfile ) {
296
+ containerfile = filepath .Clean (filepath .FromSlash (containerfile ))
297
+ } else {
298
+ containerfile = filepath .Join (buildContext .ContextDirectory ,
299
+ filepath .Clean (filepath .FromSlash (containerfile )))
300
+ }
263
301
}
264
302
buildContext .ContainerFiles = append (buildContext .ContainerFiles , containerfile )
265
303
}
@@ -819,7 +857,110 @@ func executeBuild(runtime *libpod.Runtime, w http.ResponseWriter, r *http.Reques
819
857
}
820
858
}
821
859
860
+ // handleLocalBuildContexts processes build contexts for local builds using filesystem paths.
861
+ // It extracts the local context directory and processes additional build contexts.
862
+ func handleLocalBuildContexts (query url.Values , anchorDir string ) (* BuildContext , error ) {
863
+ localContextDir := query .Get ("localcontextdir" )
864
+ if localContextDir == "" {
865
+ return nil , utils .GetBadRequestError ("localcontextdir" , localContextDir , fmt .Errorf ("localcontextdir cannot be empty" ))
866
+ }
867
+ localContextDir = filepath .Clean (localContextDir )
868
+ if err := validateLocalPath (localContextDir ); err != nil {
869
+ if errors .Is (err , os .ErrNotExist ) {
870
+ return nil , utils .GetFileNotFoundError (err )
871
+ }
872
+ return nil , utils .GetGenericBadRequestError (err )
873
+ }
874
+
875
+ out := & BuildContext {
876
+ ContextDirectory : localContextDir ,
877
+ AdditionalBuildContexts : make (map [string ]* buildahDefine.AdditionalBuildContext ),
878
+ }
879
+
880
+ for _ , url := range query ["additionalbuildcontexts" ] {
881
+ name , value , found := strings .Cut (url , "=" )
882
+ if ! found {
883
+ return nil , utils .GetInternalServerError (fmt .Errorf ("additionalbuildcontexts must be in name=value format: %q" , url ))
884
+ }
885
+
886
+ logrus .Debugf ("name: %q, context: %q" , name , value )
887
+
888
+ switch {
889
+ case strings .HasPrefix (value , "url:" ):
890
+ value = strings .TrimPrefix (value , "url:" )
891
+ tempDir , subdir , err := buildahDefine .TempDirForURL (anchorDir , "buildah" , value )
892
+ if err != nil {
893
+ return nil , utils .GetInternalServerError (genSpaceErr (err ))
894
+ }
895
+
896
+ contextPath := filepath .Join (tempDir , subdir )
897
+ out .AdditionalBuildContexts [name ] = & buildahDefine.AdditionalBuildContext {
898
+ IsURL : true ,
899
+ IsImage : false ,
900
+ Value : contextPath ,
901
+ DownloadedCache : contextPath ,
902
+ }
903
+ case strings .HasPrefix (value , "image:" ):
904
+ value = strings .TrimPrefix (value , "image:" )
905
+ out .AdditionalBuildContexts [name ] = & buildahDefine.AdditionalBuildContext {
906
+ IsURL : false ,
907
+ IsImage : true ,
908
+ Value : value ,
909
+ }
910
+ case strings .HasPrefix (value , "localpath:" ):
911
+ value = strings .TrimPrefix (value , "localpath:" )
912
+ out .AdditionalBuildContexts [name ] = & buildahDefine.AdditionalBuildContext {
913
+ IsURL : false ,
914
+ IsImage : false ,
915
+ Value : filepath .Clean (value ),
916
+ }
917
+ }
918
+ }
919
+ return out , nil
920
+ }
921
+
922
+ // getLocalBuildContext processes build contexts from HTTP request to a BuildContext struct.
923
+ func getLocalBuildContext (r * http.Request , query url.Values , anchorDir string , multipart bool ) (* BuildContext , error ) {
924
+ // Handle build contexts (extract from tar/multipart)
925
+ buildContext , err := handleLocalBuildContexts (query , anchorDir )
926
+ if err != nil {
927
+ return nil , err
928
+ }
929
+
930
+ // Process build context and container files
931
+ buildContext , err = processBuildContext (query , r , buildContext , anchorDir )
932
+ if err != nil {
933
+ return nil , err
934
+ }
935
+
936
+ if err := buildContext .validatePaths (); err != nil {
937
+ if errors .Is (err , os .ErrNotExist ) {
938
+ return nil , utils .GetFileNotFoundError (err )
939
+ }
940
+ return nil , utils .GetGenericBadRequestError (err )
941
+ }
942
+
943
+ // Process dockerignore
944
+ _ , ignoreFile , err := util .ParseDockerignore (buildContext .ContainerFiles , buildContext .ContextDirectory )
945
+ if err != nil {
946
+ return nil , utils .GetInternalServerError (fmt .Errorf ("processing ignore file: %w" , err ))
947
+ }
948
+ buildContext .IgnoreFile = ignoreFile
949
+
950
+ return buildContext , nil
951
+ }
952
+
953
+ func LocalBuildImage (w http.ResponseWriter , r * http.Request ) {
954
+ buildImage (w , r , getLocalBuildContext )
955
+ }
956
+
822
957
func BuildImage (w http.ResponseWriter , r * http.Request ) {
958
+ buildImage (w , r , getBuildContext )
959
+ }
960
+
961
+ type getBuildContextFunc func (r * http.Request , query url.Values , anchorDir string , multipart bool ) (* BuildContext , error )
962
+
963
+ func buildImage (w http.ResponseWriter , r * http.Request , getBuildContextFunc getBuildContextFunc ) {
823
964
// Create temporary directory for build context
824
965
anchorDir , err := os .MkdirTemp (parse .GetTempDir (), "libpod_builder" )
825
966
if err != nil {
@@ -850,7 +991,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
850
991
}
851
992
queryValues := r .URL .Query ()
852
993
853
- buildContext , err := getBuildContext (r , queryValues , anchorDir , multipart )
994
+ buildContext , err := getBuildContextFunc (r , queryValues , anchorDir , multipart )
854
995
if err != nil {
855
996
utils .ProcessBuildError (w , err )
856
997
return
0 commit comments