Add 'whenjobs --whisper' which lets you set variables "quietly".
authorRichard W.M. Jones <rjones@redhat.com>
Tue, 13 Mar 2012 17:58:52 +0000 (17:58 +0000)
committerRichard W.M. Jones <rjones@redhat.com>
Tue, 13 Mar 2012 18:27:46 +0000 (18:27 +0000)
No jobs resulting from the changed variables will run.

daemon/daemon.ml
lib/whenproto.x
tests/jobs/Makefile.am
tests/jobs/t103_whisper.ml [new file with mode: 0644]
tests/jobs/t103_whisper.ml.expected [new file with mode: 0644]
tools/whenjobs.ml
tools/whenjobs.pod

index cf14188..e18574d 100644 (file)
@@ -83,6 +83,7 @@ let rec init j d =
       ~proc_get_job_names
       ~proc_test_variables
       ~proc_ping_daemon
+      ~proc_whisper_variables
       (Rpc_server.Unix addr)
       Rpc.Tcp (* not TCP, this is the same as SOCK_STREAM *)
       Rpc.Socket
@@ -259,6 +260,36 @@ and proc_test_variables vars =
 
 and proc_ping_daemon () = `ok
 
+and proc_whisper_variables vars =
+  try
+    let vars = Array.map (
+      fun { Whenproto_aux.sv_name = name; sv_value = value } ->
+        name, variable_of_rpc value
+    ) vars in
+    let vars = Array.to_list vars in
+
+    if !debug then
+      Syslog.notice "remote call: whisper_variables (%s)"
+        (String.concat " "
+           (List.map (
+             fun (name, value) ->
+               sprintf "%s=%s" name (string_of_variable value)
+            ) vars));
+
+    List.iter (fun (name, _) -> check_valid_variable_name name) vars;
+
+    (* Update all the variables atomically. *)
+    let s = List.fold_left (
+      fun s (name, value) -> Whenstate.set_variable s name value
+    ) !state vars in
+    state := s;
+
+    (* .. but don't reevaluate or run jobs. *)
+
+    `ok
+  with
+    Failure msg -> `error msg
+
 (* Reload the jobs file. *)
 and reload_file () =
   let file = sprintf "%s/jobs.cmo" !jobsdir in
index ff5edfb..ad44e65 100644 (file)
@@ -103,5 +103,6 @@ program When {
     job_name_list get_job_names (void) = 11;
     job_name_list test_variables (set_variable_list) = 12;
     status ping_daemon (void) = 13;
+    status whisper_variables (set_variable_list) = 14;
   } = 1;
 } = 0x20008081;
index d80c09f..1ae468b 100644 (file)
@@ -22,6 +22,7 @@ TESTS = \
        t100_counter.ml \
        t101_updown.ml \
        t102_manyjobs.ml \
+       t103_whisper.ml \
        t200_ocaml_jobnames.ml \
        t201_ocaml_set_variable.ml
 
diff --git a/tests/jobs/t103_whisper.ml b/tests/jobs/t103_whisper.ml
new file mode 100644 (file)
index 0000000..a6da8bb
--- /dev/null
@@ -0,0 +1,56 @@
+(* whenjobs
+ * Copyright (C) 2012 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+when state == "" :
+<<
+  whenjobs --set state=A
+>>
+
+when state == "A" :
+<<
+  echo state A >\> $HOME/test_output
+  sleep 1
+  whenjobs --set state=B
+>>
+
+when state == "B" :
+<<
+  echo state B >\> $HOME/test_output
+  sleep 1
+  # This shouldn't cause state A to run:
+  whenjobs --whisper state=A
+  sleep 1
+  whenjobs --set state=C
+>>
+
+when state == "C":
+<<
+  echo state C >\> $HOME/test_output
+  sleep 1
+  # This shouldn't cause state B to run:
+  whenjobs --whisper state=B
+  sleep 1
+  whenjobs --set state=D
+>>
+
+when state == "D":
+<<
+  echo state D >\> $HOME/test_output
+  sleep 1
+  whenjobs --daemon-stop
+>>
diff --git a/tests/jobs/t103_whisper.ml.expected b/tests/jobs/t103_whisper.ml.expected
new file mode 100644 (file)
index 0000000..1c046cd
--- /dev/null
@@ -0,0 +1,4 @@
+state A
+state B
+state C
+state D
index f77e825..1334aec 100644 (file)
@@ -116,12 +116,13 @@ let rec main () =
     "--variables", Arg.Unit (set_mode `Variables), " Display all variables and values";
     "-V", Arg.Unit display_version, " Display version number and exit";
     "--version", Arg.Unit display_version, " Display version number and exit";
+    "--whisper", Arg.Unit (set_mode `Whisper), " Set the variable, quietly";
   ] in
 
   (* anon_fun normally just collects up the anonymous arguments as
    * strings, and most modes just use 'args' as a list of strings.
-   * However for `Set and `Test modes we need to record the type of
-   * each argument as well, so we keep that in a separate list
+   * However for `Set, `Test and `Whisper modes we need to record the
+   * type of each argument as well, so we keep that in a separate list
    * (argtypes).
    *)
   let argtypes = ref [] in
@@ -195,6 +196,10 @@ Options:
     if nr_args > 0 then
       test_variables argtypes
 
+  | Some `Whisper ->
+    if nr_args > 0 then
+      whisper_variables argtypes
+
   | Some `Get ->
     if nr_args != 1 then (
       eprintf "whenjobs --get variable\n";
@@ -374,7 +379,7 @@ and test_variables argtypes =
       let i =
         try String.index def '='
         with Not_found ->
-          eprintf "whenjobs: set: missing = sign in variable definition\n";
+          eprintf "whenjobs: test: missing = sign in variable definition\n";
           suggest_help ();
           exit 1 in
       let name = String.sub def 0 i in
@@ -390,6 +395,35 @@ and test_variables argtypes =
 
   Array.iter print_endline jobnames
 
+and whisper_variables argtypes =
+  let vars = List.map (
+    fun (def, typ) ->
+      (* 'def' should have the form "name=value".  The value part may
+       * be missing, but the equals sign is required.
+       *)
+      let i =
+        try String.index def '='
+        with Not_found ->
+          eprintf "whenjobs: whisper: missing = sign in variable definition\n";
+          suggest_help ();
+          exit 1 in
+      let name = String.sub def 0 i in
+      let value = String.sub def (i+1) (String.length def - (i+1)) in
+      let value = value_of_string value typ in
+      { Whenproto_aux.sv_name = name; sv_value = value }
+  ) argtypes in
+  let vars = Array.of_list vars in
+
+  let client = start_client () in
+  (match Whenproto_clnt.When.V1.whisper_variables client vars with
+  | `ok -> ()
+  | `error msg ->
+    eprintf "whenjobs: whisper: %s\n" msg;
+    suggest_check_server_logs ();
+    exit 1
+  );
+  stop_client client
+
 and get_variable name =
   let client = start_client () in
   let value = Whenproto_clnt.When.V1.get_variable client name in
index 14ca141..6d2a554 100644 (file)
@@ -267,6 +267,25 @@ Display all the variables and their values, in the format C<name=value>.
 
 Display the name and version of the program and exit.
 
+=item B<--whisper> variable=value [variable=value ...]
+
+This works the same way as the I<--set> option, but with the
+difference that jobs' when clauses are not reevaluated.  In other
+words, the variables are set, but "quietly" so as not to trigger any
+jobs to run.
+
+Note that this can lead to some unexpected results: one case is a
+when job such as:
+
+ when changed a || changed b : << ... >>
+
+If C<a> is changed using I<--whisper>, then the job will not run.
+
+But later on, if C<b> is set but to the same value that it already has
+(ie. not changed), the job will run because the whole when-clause is
+reevaluated and C<a> is found to have changed since the last run of
+the job.
+
 =back
 
 =head1 REFERENCE