Pino Toscano
2014-Apr-15  12:15 UTC
[Libguestfs] [PATCH] builder: add per-repository proxy configuration
Add the possibility to configure the proxy in each repository .conf
file, specifying whether use no proxy at all, follow the system
configuration or use a specific proxy.
---
 builder/builder.ml       | 10 +++++-----
 builder/downloader.ml    | 44 ++++++++++++++++++++++++++++++++++++--------
 builder/downloader.mli   | 15 +++++++++++++--
 builder/index_parser.ml  |  4 ++--
 builder/index_parser.mli |  2 +-
 builder/list_entries.ml  |  4 ++--
 builder/list_entries.mli |  2 +-
 builder/sources.ml       | 12 +++++++++++-
 builder/sources.mli      |  1 +
 builder/virt-builder.pod | 25 +++++++++++++++++++++++++
 10 files changed, 97 insertions(+), 22 deletions(-)
diff --git a/builder/builder.ml b/builder/builder.ml
index 81eb2d9..062dbb9 100644
--- a/builder/builder.ml
+++ b/builder/builder.ml
@@ -136,25 +136,25 @@ let main ()    let downloader = Downloader.create ~debug
~curl ~cache in
   let repos = Sources.read_sources ~prog ~debug in
   let repos = List.map (
-    fun { Sources.uri = uri; Sources.gpgkey = gpgkey } ->
+    fun { Sources.uri = uri; Sources.gpgkey = gpgkey; Sources.proxy = proxy }
->
       let gpgkey          match gpgkey with
         | None -> Sigchecker.No_Key
         | Some key -> Sigchecker.KeyFile key in
-      uri, gpgkey
+      uri, gpgkey, proxy
   ) repos in
   let sources = List.map (
     fun (source, fingerprint) ->
-      source, Sigchecker.Fingerprint fingerprint
+      source, Sigchecker.Fingerprint fingerprint, Downloader.SystemProxy
   ) sources in
   let sources = List.append repos sources in
   let index : Index_parser.index      List.concat (
       List.map (
-        fun (source, key) ->
+        fun (source, key, proxy) ->
           let sigchecker              Sigchecker.create ~debug ~gpg
~check_signature ~gpgkey:key in
-          Index_parser.get_index ~prog ~debug ~downloader ~sigchecker source
+          Index_parser.get_index ~prog ~debug ~downloader ~sigchecker ~proxy
source
       ) sources
     ) in
 
diff --git a/builder/downloader.ml b/builder/downloader.ml
index e23cb37..f8cd7ab 100644
--- a/builder/downloader.ml
+++ b/builder/downloader.ml
@@ -37,25 +37,30 @@ type t = {
   cache : string option;                (* cache directory for templates *)
 }
 
+type proxy_mode +  | UnsetProxy
+  | SystemProxy
+  | ForcedProxy of string
+
 let create ~debug ~curl ~cache = {
   debug = debug;
   curl = curl;
   cache = cache;
 }
 
-let rec download ~prog t ?template ?progress_bar uri +let rec download ~prog t
?template ?progress_bar ?(proxy = SystemProxy) uri    match template with
   | None ->                       (* no cache, simple download *)
     (* Create a temporary name. *)
     let tmpfile = Filename.temp_file "vbcache" ".txt" in
-    download_to ~prog t ?progress_bar uri tmpfile;
+    download_to ~prog t ?progress_bar ~proxy uri tmpfile;
     (tmpfile, true)
 
   | Some (name, arch, revision) ->
     match t.cache with
     | None ->
       (* Not using the cache at all? *)
-      download t ~prog ?progress_bar uri
+      download t ~prog ?progress_bar ~proxy uri
 
     | Some cachedir ->
       let filename = cache_of_name cachedir name arch revision in
@@ -64,11 +69,11 @@ let rec download ~prog t ?template ?progress_bar uri        
* If not, download it.
        *)
       if not (Sys.file_exists filename) then
-        download_to ~prog t ?progress_bar uri filename;
+        download_to ~prog t ?progress_bar ~proxy uri filename;
 
       (filename, false)
 
-and download_to ~prog t ?(progress_bar = false) uri filename +and download_to
~prog t ?(progress_bar = false) ~proxy uri filename    let parseduri      try
URI.parse_uri uri
     with Invalid_argument "URI.parse_uri" ->
@@ -95,9 +100,11 @@ and download_to ~prog t ?(progress_bar = false) uri filename
prog path;
       exit 1
     )
-  | _ -> (* Any other protocol. *)
+  | _ as protocol -> (* Any other protocol. *)
+    let outenv = proxy_envvar protocol proxy in
     (* Get the status code first to ensure the file exists. *)
-    let cmd = sprintf "%s%s -g -o /dev/null -I -w '%%{http_code}'
%s"
+    let cmd = sprintf "%s%s%s -g -o /dev/null -I -w
'%%{http_code}' %s"
+      outenv
       t.curl
       (if t.debug then "" else " -s -S")
       (quote uri) in
@@ -122,7 +129,8 @@ and download_to ~prog t ?(progress_bar = false) uri filename
);
 
     (* Now download the file. *)
-    let cmd = sprintf "%s%s -g -o %s %s"
+    let cmd = sprintf "%s%s%s -g -o %s %s"
+      outenv
       t.curl
       (if t.debug then "" else if progress_bar then " -#"
else " -s -S")
       (quote filename_new) (quote uri) in
@@ -137,3 +145,23 @@ and download_to ~prog t ?(progress_bar = false) uri
filename
   (* Rename the file if the download was successful. *)
   rename filename_new filename
+
+and proxy_envvar protocol = function
+  | UnsetProxy ->
+    (match protocol with
+    | "http" -> "env http_proxy= no_proxy=* "
+    | "https" -> "env https_proxy= no_proxy=* "
+    | "ftp" -> "env ftp_proxy= no_proxy=* "
+    | _ -> "env no_proxy=* "
+    )
+  | SystemProxy ->
+    (* No changes required. *)
+    ""
+  | ForcedProxy proxy ->
+    let proxy = Filename.quote proxy in
+    (match protocol with
+    | "http" -> sprintf "env http_proxy=%s no_proxy= "
proxy
+    | "https" -> sprintf "env https_proxy=%s no_proxy= "
proxy
+    | "ftp" -> sprintf "env ftp_proxy=%s no_proxy= "
proxy
+    | _ -> ""
+    )
diff --git a/builder/downloader.mli b/builder/downloader.mli
index 8daa661..4d24a34 100644
--- a/builder/downloader.mli
+++ b/builder/downloader.mli
@@ -29,10 +29,18 @@ type filename = string
 type t
 (** The abstract data type. *)
 
+(** Type of proxy. *)
+type proxy_mode +  | UnsetProxy                 (* The proxy is forced off. *)
+  | SystemProxy                (* The proxy is not changed (follows the
+                                * system configuration).
+                                *)
+  | ForcedProxy of string      (* The proxy is forced to the specified URL. *)
+
 val create : debug:bool -> curl:string -> cache:string option -> t
 (** Create the abstract type. *)
 
-val download : prog:string -> t -> ?template:(string*string*int) ->
?progress_bar:bool -> uri -> (filename * bool)
+val download : prog:string -> t -> ?template:(string*string*int) ->
?progress_bar:bool -> ?proxy:proxy_mode -> uri -> (filename * bool)
 (** Download the URI, returning the downloaded filename and a
     temporary file flag.  The temporary file flag is [true] iff
     the downloaded file is temporary and should be deleted by the
@@ -44,4 +52,7 @@ val download : prog:string -> t ->
?template:(string*string*int) -> ?progress_ba
 
     If [~progress_bar:true] then display a progress bar if the file
     doesn't come from the cache.  In debug mode, progress messages
-    are always displayed. *)
+    are always displayed.
+
+    [proxy] specifies the type of proxy to be used in the transfer,
+    if possible. *)
diff --git a/builder/index_parser.ml b/builder/index_parser.ml
index 5d566f9..2040656 100644
--- a/builder/index_parser.ml
+++ b/builder/index_parser.ml
@@ -102,7 +102,7 @@ let print_entry chan (name, { printable_name =
printable_name;
   ) notes;
   if hidden then fp "hidden=true\n"
 
-let get_index ~prog ~debug ~downloader ~sigchecker source +let get_index ~prog
~debug ~downloader ~sigchecker ~proxy source    let corrupt_file ()      eprintf
(f_"\nThe index file downloaded from '%s' is corrupt.\nYou need to
ask the supplier of this file to fix it and upload a fixed version.\n")
       source;
@@ -111,7 +111,7 @@ let get_index ~prog ~debug ~downloader ~sigchecker source  
   let rec get_index ()      (* Get the index page. *)
-    let tmpfile, delete_tmpfile = Downloader.download ~prog downloader source
in
+    let tmpfile, delete_tmpfile = Downloader.download ~prog downloader ~proxy
source in
 
     (* Check index file signature (also verifies it was fully
      * downloaded and not corrupted in transit).
diff --git a/builder/index_parser.mli b/builder/index_parser.mli
index 0575dc4..c2c5d11 100644
--- a/builder/index_parser.mli
+++ b/builder/index_parser.mli
@@ -36,4 +36,4 @@ and entry = {
   sigchecker : Sigchecker.t;
 }
 
-val get_index : prog:string -> debug:bool -> downloader:Downloader.t
-> sigchecker:Sigchecker.t -> string -> index
+val get_index : prog:string -> debug:bool -> downloader:Downloader.t
-> sigchecker:Sigchecker.t -> proxy:Downloader.proxy_mode -> string
-> index
diff --git a/builder/list_entries.ml b/builder/list_entries.ml
index 2c600d5..505a1b9 100644
--- a/builder/list_entries.ml
+++ b/builder/list_entries.ml
@@ -47,7 +47,7 @@ and list_entries_long ~sources index    let langs =
Languages.languages () in
 
   List.iter (
-    fun (source, key) ->
+    fun (source, key, proxy) ->
       printf (f_"Source URI: %s\n") source;
       (match key with
       | Sigchecker.No_Key -> ()
@@ -136,7 +136,7 @@ and list_entries_json ~sources index    printf " 
\"version\": %d,\n" 1;
   printf "  \"sources\": [\n";
   iteri (
-    fun i (source, key) ->
+    fun i (source, key, proxy) ->
       printf "  {\n";
       (match key with
       | Sigchecker.No_Key -> ()
diff --git a/builder/list_entries.mli b/builder/list_entries.mli
index b53ccec..ce012c4 100644
--- a/builder/list_entries.mli
+++ b/builder/list_entries.mli
@@ -16,4 +16,4 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *)
 
-val list_entries : list_format:([ `Short | `Long | `Json ]) ->
sources:(string * Sigchecker.gpgkey_type) list -> Index_parser.index ->
unit
+val list_entries : list_format:([ `Short | `Long | `Json ]) ->
sources:(string * Sigchecker.gpgkey_type * Downloader.proxy_mode) list ->
Index_parser.index -> unit
diff --git a/builder/sources.ml b/builder/sources.ml
index 90716bd..e7644a2 100644
--- a/builder/sources.ml
+++ b/builder/sources.ml
@@ -26,6 +26,7 @@ type source = {
   name : string;
   uri : string;
   gpgkey : string option;
+  proxy : Downloader.proxy_mode;
 }
 
 module StringSet = Set.Make (String)
@@ -65,8 +66,17 @@ let parse_conf ~prog ~debug file                );
               None
             ) in
+        let proxy +          try
+            (match (List.assoc ("proxy", None) fields) with
+            | "no" | "off" -> Downloader.UnsetProxy
+            | "system" -> Downloader.SystemProxy
+            | _ as proxy -> Downloader.ForcedProxy proxy
+            )
+          with
+            Not_found -> Downloader.SystemProxy in
         {
-          name = n; uri = uri; gpgkey = gpgkey;
+          name = n; uri = uri; gpgkey = gpgkey; proxy = proxy;
         }
       in
       try (give_source n fields) :: acc
diff --git a/builder/sources.mli b/builder/sources.mli
index 76feeda..0ade536 100644
--- a/builder/sources.mli
+++ b/builder/sources.mli
@@ -20,6 +20,7 @@ type source = {
   name : string;
   uri : string;
   gpgkey : string option;
+  proxy : Downloader.proxy_mode;
 }
 
 val read_sources : prog:string -> debug:bool -> source list
diff --git a/builder/virt-builder.pod b/builder/virt-builder.pod
index 8d27452..ebe6528 100644
--- a/builder/virt-builder.pod
+++ b/builder/virt-builder.pod
@@ -1010,6 +1010,31 @@ This optional field represents the URI (although only
I<file://> URIs
 are accepted) of the key used to sign the index file.
 If not present, the index file referred by I<uri=..> is not signed.
 
+=item C<proxy=MODE>
+
+This optional field specifies the proxy mode, to be used when downloading
+the index file of this repository.  The possible values are:
+
+=over 4
+
+=item B<no>, B<off>
+
+No proxy is being used at all, even overriding the system configuration.
+
+=item B<system>
+
+The proxy used is the system one.
+
+=item I<anything else>
+
+Specifies the actual proxy configuration to be used, overriding the system
+configuration.
+
+=back
+
+If not present, the assumed value is to respect the proxy settings of the
+system (i.e. as if B<system> would be specified).
+
 =back
 
 For serious virt-builder use, you may want to create your own
-- 
1.9.0
Richard W.M. Jones
2014-Apr-15  14:50 UTC
Re: [Libguestfs] [PATCH] builder: add per-repository proxy configuration
ACK. Thanks, Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com virt-p2v converts physical machines to virtual machines. Boot with a live CD or over the network (PXE) and turn machines into KVM guests. http://libguestfs.org/virt-v2v