Forráskód Böngészése

HUE-847 [oozie] File selector corrupting the fields

'files' and 'archives' were not loaded when editing an action
Selecting a field trough the fileselector was not detected by ko
Fixing also Jobsub for this
Convert from CharField to TextField 'files' and 'archives' of an Action
Set to None incomplete properties of an executed Workflow but not present in the DB anymore
Adding some tests
Romain Rigaux 13 éve
szülő
commit
8b74fb02e2

+ 1 - 0
apps/jobsub/src/jobsub/templates/edit_design.mako

@@ -312,6 +312,7 @@ ${layout.menubar(section='designs')}
                     initialPath: inputElement.val(),
                     onFileChoose: function(filePath) {
                         inputElement.val(filePath);
+                        inputElement.change();
                         $("#chooseFile").modal("hide");
                     },
                     createFolder: false

+ 333 - 0
apps/oozie/src/oozie/migrations/0006_auto__chg_field_java_files__chg_field_java_archives__chg_field_sqoop_f.py

@@ -0,0 +1,333 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+    
+    def forwards(self, orm):
+        
+        # Changing field 'Java.files'
+        db.alter_column('oozie_java', 'files', self.gf('django.db.models.fields.TextField')())
+
+        # Changing field 'Java.archives'
+        db.alter_column('oozie_java', 'archives', self.gf('django.db.models.fields.TextField')())
+
+        # Changing field 'Sqoop.files'
+        db.alter_column('oozie_sqoop', 'files', self.gf('django.db.models.fields.TextField')())
+
+        # Changing field 'Sqoop.archives'
+        db.alter_column('oozie_sqoop', 'archives', self.gf('django.db.models.fields.TextField')())
+
+        # Changing field 'Pig.files'
+        db.alter_column('oozie_pig', 'files', self.gf('django.db.models.fields.TextField')())
+
+        # Changing field 'Pig.archives'
+        db.alter_column('oozie_pig', 'archives', self.gf('django.db.models.fields.TextField')())
+
+        # Changing field 'Mapreduce.files'
+        db.alter_column('oozie_mapreduce', 'files', self.gf('django.db.models.fields.TextField')())
+
+        # Changing field 'Mapreduce.archives'
+        db.alter_column('oozie_mapreduce', 'archives', self.gf('django.db.models.fields.TextField')())
+
+        # Changing field 'Streaming.files'
+        db.alter_column('oozie_streaming', 'files', self.gf('django.db.models.fields.TextField')())
+
+        # Changing field 'Streaming.archives'
+        db.alter_column('oozie_streaming', 'archives', self.gf('django.db.models.fields.TextField')())
+
+        # Changing field 'Shell.files'
+        db.alter_column('oozie_shell', 'files', self.gf('django.db.models.fields.TextField')())
+
+        # Changing field 'Shell.archives'
+        db.alter_column('oozie_shell', 'archives', self.gf('django.db.models.fields.TextField')())
+
+        # Changing field 'Hive.files'
+        db.alter_column('oozie_hive', 'files', self.gf('django.db.models.fields.TextField')())
+
+        # Changing field 'Hive.archives'
+        db.alter_column('oozie_hive', 'archives', self.gf('django.db.models.fields.TextField')())
+    
+    
+    def backwards(self, orm):
+        
+        # Changing field 'Java.files'
+        db.alter_column('oozie_java', 'files', self.gf('django.db.models.fields.CharField')(max_length=512))
+
+        # Changing field 'Java.archives'
+        db.alter_column('oozie_java', 'archives', self.gf('django.db.models.fields.CharField')(max_length=512))
+
+        # Changing field 'Sqoop.files'
+        db.alter_column('oozie_sqoop', 'files', self.gf('django.db.models.fields.CharField')(max_length=512))
+
+        # Changing field 'Sqoop.archives'
+        db.alter_column('oozie_sqoop', 'archives', self.gf('django.db.models.fields.CharField')(max_length=512))
+
+        # Changing field 'Pig.files'
+        db.alter_column('oozie_pig', 'files', self.gf('django.db.models.fields.CharField')(max_length=512))
+
+        # Changing field 'Pig.archives'
+        db.alter_column('oozie_pig', 'archives', self.gf('django.db.models.fields.CharField')(max_length=512))
+
+        # Changing field 'Mapreduce.files'
+        db.alter_column('oozie_mapreduce', 'files', self.gf('django.db.models.fields.CharField')(max_length=512))
+
+        # Changing field 'Mapreduce.archives'
+        db.alter_column('oozie_mapreduce', 'archives', self.gf('django.db.models.fields.CharField')(max_length=512))
+
+        # Changing field 'Streaming.files'
+        db.alter_column('oozie_streaming', 'files', self.gf('django.db.models.fields.CharField')(max_length=512))
+
+        # Changing field 'Streaming.archives'
+        db.alter_column('oozie_streaming', 'archives', self.gf('django.db.models.fields.CharField')(max_length=512))
+
+        # Changing field 'Shell.files'
+        db.alter_column('oozie_shell', 'files', self.gf('django.db.models.fields.CharField')(max_length=512))
+
+        # Changing field 'Shell.archives'
+        db.alter_column('oozie_shell', 'archives', self.gf('django.db.models.fields.CharField')(max_length=512))
+
+        # Changing field 'Hive.files'
+        db.alter_column('oozie_hive', 'files', self.gf('django.db.models.fields.CharField')(max_length=512))
+
+        # Changing field 'Hive.archives'
+        db.alter_column('oozie_hive', 'archives', self.gf('django.db.models.fields.CharField')(max_length=512))
+    
+    
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'oozie.coordinator': {
+            'Meta': {'object_name': 'Coordinator', '_ormbases': ['oozie.Job']},
+            'concurrency': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'end': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 9, 23, 14, 58, 35, 963525)'}),
+            'execution': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
+            'frequency_number': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+            'frequency_unit': ('django.db.models.fields.CharField', [], {'default': "'days'", 'max_length': '20'}),
+            'job_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['oozie.Job']", 'unique': 'True', 'primary_key': 'True'}),
+            'start': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 9, 20, 14, 58, 35, 963495)'}),
+            'throttle': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'timeout': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'timezone': ('django.db.models.fields.CharField', [], {'default': "'America/Los_Angeles'", 'max_length': '24'}),
+            'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['oozie.Workflow']", 'null': 'True'})
+        },
+        'oozie.datainput': {
+            'Meta': {'object_name': 'DataInput'},
+            'coordinator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['oozie.Coordinator']"}),
+            'dataset': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['oozie.Dataset']", 'unique': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '40'})
+        },
+        'oozie.dataoutput': {
+            'Meta': {'object_name': 'DataOutput'},
+            'coordinator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['oozie.Coordinator']"}),
+            'dataset': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['oozie.Dataset']", 'unique': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '40'})
+        },
+        'oozie.dataset': {
+            'Meta': {'object_name': 'Dataset'},
+            'coordinator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['oozie.Coordinator']"}),
+            'description': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
+            'done_flag': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64', 'blank': 'True'}),
+            'frequency_number': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+            'frequency_unit': ('django.db.models.fields.CharField', [], {'default': "'days'", 'max_length': '20'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+            'start': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 9, 20, 14, 58, 35, 964734)'}),
+            'timezone': ('django.db.models.fields.CharField', [], {'default': "'America/Los_Angeles'", 'max_length': '24'}),
+            'uri': ('django.db.models.fields.CharField', [], {'default': "'/data/${YEAR}${MONTH}${DAY}'", 'max_length': '1024'})
+        },
+        'oozie.end': {
+            'Meta': {'object_name': 'End'},
+            'node_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['oozie.Node']", 'unique': 'True', 'primary_key': 'True'})
+        },
+        'oozie.fork': {
+            'Meta': {'object_name': 'Fork'},
+            'node_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['oozie.Node']", 'unique': 'True', 'primary_key': 'True'})
+        },
+        'oozie.history': {
+            'Meta': {'object_name': 'History'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'job': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['oozie.Job']"}),
+            'oozie_job_id': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'properties': ('django.db.models.fields.TextField', [], {}),
+            'submission_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
+            'submitter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'oozie.hive': {
+            'Meta': {'object_name': 'Hive'},
+            'archives': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'files': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'job_properties': ('django.db.models.fields.TextField', [], {'default': '\'[{"name":"oozie.use.system.libpath","value":"true"},{"name":"oozie.hive.defaults","value":"${hive.default.xml}"}]\''}),
+            'job_xml': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '512', 'blank': 'True'}),
+            'node_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['oozie.Node']", 'unique': 'True', 'primary_key': 'True'}),
+            'params': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'prepares': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'script_path': ('django.db.models.fields.CharField', [], {'max_length': '256'})
+        },
+        'oozie.java': {
+            'Meta': {'object_name': 'Java'},
+            'archives': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'args': ('django.db.models.fields.CharField', [], {'max_length': '4096', 'blank': 'True'}),
+            'files': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'jar_path': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'java_opts': ('django.db.models.fields.CharField', [], {'max_length': '256', 'blank': 'True'}),
+            'job_properties': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'job_xml': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '512', 'blank': 'True'}),
+            'main_class': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
+            'node_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['oozie.Node']", 'unique': 'True', 'primary_key': 'True'}),
+            'prepares': ('django.db.models.fields.TextField', [], {'default': "'[]'"})
+        },
+        'oozie.job': {
+            'Meta': {'object_name': 'Job'},
+            'deployment_dir': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
+            'description': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_shared': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True', 'blank': 'True'}),
+            'last_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+            'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+            'parameters': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'schema_version': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+        },
+        'oozie.join': {
+            'Meta': {'object_name': 'Join'},
+            'node_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['oozie.Node']", 'unique': 'True', 'primary_key': 'True'})
+        },
+        'oozie.kill': {
+            'Meta': {'object_name': 'Kill'},
+            'message': ('django.db.models.fields.CharField', [], {'default': "'Action failed, error message[${wf:errorMessage(wf:lastErrorNode())}]'", 'max_length': '256'}),
+            'node_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['oozie.Node']", 'unique': 'True', 'primary_key': 'True'})
+        },
+        'oozie.link': {
+            'Meta': {'object_name': 'Link'},
+            'child': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'parent_node'", 'to': "orm['oozie.Node']"}),
+            'comment': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'child_node'", 'to': "orm['oozie.Node']"})
+        },
+        'oozie.mapreduce': {
+            'Meta': {'object_name': 'Mapreduce'},
+            'archives': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'files': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'jar_path': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'job_properties': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'job_xml': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '512', 'blank': 'True'}),
+            'node_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['oozie.Node']", 'unique': 'True'}),
+            'prepares': ('django.db.models.fields.TextField', [], {'default': "'[]'"})
+        },
+        'oozie.node': {
+            'Meta': {'object_name': 'Node'},
+            'children': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'parents'", 'symmetrical': 'False', 'through': "orm['oozie.Link']", 'to': "orm['oozie.Node']"}),
+            'description': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+            'node_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+            'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['oozie.Workflow']"})
+        },
+        'oozie.pig': {
+            'Meta': {'object_name': 'Pig'},
+            'archives': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'files': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'job_properties': ('django.db.models.fields.TextField', [], {'default': '\'[{"name":"oozie.use.system.libpath","value":"true"}]\''}),
+            'job_xml': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '512', 'blank': 'True'}),
+            'node_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['oozie.Node']", 'unique': 'True', 'primary_key': 'True'}),
+            'params': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'prepares': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'script_path': ('django.db.models.fields.CharField', [], {'max_length': '256'})
+        },
+        'oozie.shell': {
+            'Meta': {'object_name': 'Shell'},
+            'archives': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'capture_output': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+            'command': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
+            'files': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'job_properties': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'job_xml': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '512', 'blank': 'True'}),
+            'node_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['oozie.Node']", 'unique': 'True', 'primary_key': 'True'}),
+            'params': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'prepares': ('django.db.models.fields.TextField', [], {'default': "'[]'"})
+        },
+        'oozie.sqoop': {
+            'Meta': {'object_name': 'Sqoop'},
+            'archives': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'files': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'job_properties': ('django.db.models.fields.TextField', [], {'default': '\'[{"name":"oozie.use.system.libpath","value":"true"}]\''}),
+            'job_xml': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '512', 'blank': 'True'}),
+            'node_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['oozie.Node']", 'unique': 'True', 'primary_key': 'True'}),
+            'params': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'prepares': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'script_path': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'})
+        },
+        'oozie.ssh': {
+            'Meta': {'object_name': 'Ssh'},
+            'capture_output': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+            'command': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
+            'host': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
+            'node_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['oozie.Node']", 'unique': 'True', 'primary_key': 'True'}),
+            'params': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'user': ('django.db.models.fields.CharField', [], {'max_length': '64'})
+        },
+        'oozie.start': {
+            'Meta': {'object_name': 'Start'},
+            'node_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['oozie.Node']", 'unique': 'True'})
+        },
+        'oozie.streaming': {
+            'Meta': {'object_name': 'Streaming'},
+            'archives': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'files': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'job_properties': ('django.db.models.fields.TextField', [], {'default': '\'[{"name":"oozie.use.system.libpath","value":"true"}]\''}),
+            'mapper': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'node_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['oozie.Node']", 'unique': 'True', 'primary_key': 'True'}),
+            'reducer': ('django.db.models.fields.CharField', [], {'max_length': '512'})
+        },
+        'oozie.workflow': {
+            'Meta': {'object_name': 'Workflow', '_ormbases': ['oozie.Job']},
+            'end': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'end_workflow'", 'null': 'True', 'to': "orm['oozie.End']"}),
+            'is_single': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+            'job_properties': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'job_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['oozie.Job']", 'unique': 'True', 'primary_key': 'True'}),
+            'job_xml': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '512', 'blank': 'True'}),
+            'start': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'start_workflow'", 'null': 'True', 'to': "orm['oozie.Start']"})
+        }
+    }
+    
+    complete_apps = ['oozie']

+ 14 - 14
apps/oozie/src/oozie/models.py

@@ -750,9 +750,9 @@ class Mapreduce(Action):
   PARAM_FIELDS = ('files', 'archives', 'job_properties', 'jar_path', 'prepares')
   node_type = 'mapreduce'
 
-  files = models.CharField(max_length=PATH_MAX, default="[]", verbose_name=_t('Files'),
+  files = models.TextField(default="[]", verbose_name=_t('Files'),
       help_text=_t('List of names or paths of files to be added to the distributed cache and the task running directory.'))
-  archives = models.CharField(max_length=PATH_MAX, default="[]", verbose_name=_t('Archives'),
+  archives = models.TextField(default="[]", verbose_name=_t('Archives'),
       help_text=_t('List of names or paths of the archives to be added to the distributed cache. '
                    'To force a symlink to the uncompressed archive on the task running directory, use a \'#\' '
                    'followed by the symlink name. For example \'myarch.zip#myarch\'.'))
@@ -785,9 +785,9 @@ class Streaming(Action):
   PARAM_FIELDS = ('files', 'archives', 'job_properties', 'mapper', 'reducer')
   node_type = "streaming"
 
-  files = models.CharField(max_length=PATH_MAX, default="[]", verbose_name=_t('Files'),
+  files = models.TextField(default="[]", verbose_name=_t('Files'),
       help_text=_t('List of names or paths of files to be added to the distributed cache and the task running directory.'))
-  archives = models.CharField(max_length=PATH_MAX, default="[]", verbose_name=_t('Archives'),
+  archives = models.TextField(default="[]", verbose_name=_t('Archives'),
       help_text=_t('List of names or paths of the archives to be added to the distributed cache. '
                    'To force a symlink to the uncompressed archive on the task running directory, use a \'#\' '
                    'followed by the symlink name. For example \'myarch.zip#myarch\'.'))
@@ -813,9 +813,9 @@ class Java(Action):
                   'java_opts', 'job_properties', 'prepares')
   node_type = "java"
 
-  files = models.CharField(max_length=PATH_MAX, default="[]", verbose_name=_t('Files'),
+  files = models.TextField(default="[]", verbose_name=_t('Files'),
       help_text=_t('List of names or paths of files to be added to the distributed cache and the task running directory.'))
-  archives = models.CharField(max_length=PATH_MAX, default="[]", verbose_name=_t('Archives'),
+  archives = models.TextField(default="[]", verbose_name=_t('Archives'),
       help_text=_t('List of names or paths of the archives to be added to the distributed cache. '
                    'To force a symlink to the uncompressed archive on the task running directory, use a \'#\' '
                    'followed by the symlink name. For example \'myarch.zip#myarch\'.'))
@@ -861,9 +861,9 @@ class Pig(Action):
                                  help_text=_t('Local path to the Pig script. e.g. my_script.pig'))
   params = models.TextField(default="[]", verbose_name=_t('Parameters'),
                             help_text=_t('The Pig parameters of the script. e.g. "-param", "INPUT=${inputDir}"'))
-  files = models.CharField(max_length=PATH_MAX, default="[]", verbose_name=_t('Files'),
+  files = models.TextField(default="[]", verbose_name=_t('Files'),
       help_text=_t('List of names or paths of files to be added to the distributed cache and the task running directory.'))
-  archives = models.CharField(max_length=PATH_MAX, default="[]", verbose_name=_t('Archives'),
+  archives = models.TextField(default="[]", verbose_name=_t('Archives'),
       help_text=_t('List of names or paths of the archives to be added to the distributed cache. '
                    'To force a symlink to the uncompressed archive on the task running directory, use a \'#\' '
                    'followed by the symlink name. For example \'myarch.zip#myarch\'.'))
@@ -901,9 +901,9 @@ class Hive(Action):
                                  help_text=_t('Local path to the %(type)s script. e.g. my_script.sql') % {'type': node_type.title()})
   params = models.TextField(default="[]", verbose_name=_t('Parameters'),
                             help_text=_t('The %(type)s parameters of the script. e.g. "-param", "INPUT=${inputDir}"')  % {'type': node_type.title()})
-  files = models.CharField(max_length=PATH_MAX, default="[]", verbose_name=_t('Files'),
+  files = models.TextField(default="[]", verbose_name=_t('Files'),
       help_text=_t('List of names or paths of files to be added to the distributed cache and the task running directory.'))
-  archives = models.CharField(max_length=PATH_MAX, default="[]", verbose_name=_t('Archives'),
+  archives = models.TextField(default="[]", verbose_name=_t('Archives'),
       help_text=_t('List of names or paths of the archives to be added to the distributed cache. '
                    'To force a symlink to the uncompressed archive on the task running directory, use a \'#\' '
                    'followed by the symlink name. For example \'myarch.zip#myarch\'.'))
@@ -942,9 +942,9 @@ class Sqoop(Action):
                                  help_text=_t('Local path to the %(type)s script. e.g. my_script.sql') % {'type': node_type.title()})
   params = models.TextField(default="[]", verbose_name=_t('Parameters'),
                             help_text=_t('The %(type)s parameters of the script. e.g. "import", "--connect", ".."')  % {'type': node_type.title()})
-  files = models.CharField(max_length=PATH_MAX, default="[]", verbose_name=_t('Files'),
+  files = models.TextField(default="[]", verbose_name=_t('Files'),
       help_text=_t('List of names or paths of files to be added to the distributed cache and the task running directory.'))
-  archives = models.CharField(max_length=PATH_MAX, default="[]", verbose_name=_t('Archives'),
+  archives = models.TextField(default="[]", verbose_name=_t('Archives'),
       help_text=_t('List of names or paths of the archives to be added to the distributed cache. '
                    'To force a symlink to the uncompressed archive on the task running directory, use a \'#\' '
                    'followed by the symlink name. For example \'myarch.zip#myarch\'.'))
@@ -1005,9 +1005,9 @@ class Shell(Action):
                              help_text=_t('The path of the Shell command to execute'))
   params = models.TextField(default="[]", verbose_name=_t('Arguments'),
                             help_text=_t('The arguments of Shell command can then be specified using one or more argument element.'))
-  files = models.CharField(max_length=PATH_MAX, default="[]", verbose_name=_t('Files'),
+  files = models.TextField(default="[]", verbose_name=_t('Files'),
       help_text=_t('List of names or paths of files to be added to the distributed cache and the task running directory.'))
-  archives = models.CharField(max_length=PATH_MAX, default="[]", verbose_name=_t('Archives'),
+  archives = models.TextField(default="[]", verbose_name=_t('Archives'),
       help_text=_t('List of names or paths of the archives to be added to the distributed cache. '
                    'To force a symlink to the uncompressed archive on the task running directory, use a \'#\' '
                    'followed by the symlink name. For example \'myarch.zip#myarch\'.'))

+ 9 - 7
apps/oozie/src/oozie/templates/editor/edit_workflow_action.mako

@@ -83,7 +83,7 @@ ${ layout.menubar(section='workflows') }
                     <span class="span4 required" data-bind="text: type" />
                   </td>
                   <td>
-                    <input type="text" class="input span4 required pathChooserKo" data-bind="fileChooser: $data, value: value, uniqueName: false" />
+                    <input type="text" class="input span4 required" data-bind="fileChooser: $data, value: value, uniqueName: false" />
                   </td>
                   <td><a class="btn" href="#" data-bind="click: $root.removePrepare">${ _('Delete') }</a></td>
                 </tr>
@@ -121,7 +121,7 @@ ${ layout.menubar(section='workflows') }
                     <span class="span4 required" data-bind="text: type" />
                   </td>
                   <td>
-                    <input type="text" class="input span4 required pathChooserKo" data-bind="fileChooser: $data, value: value, uniqueName: false" />
+                    <input type="text" class="input span4 required" data-bind="fileChooser: $data, value: value, uniqueName: false" />
                   </td>
                   <td><a class="btn" href="#" data-bind="click: $root.removeParam">${ _('Delete') }</a></td>
                 </tr>
@@ -166,7 +166,7 @@ ${ layout.menubar(section='workflows') }
             <tbody data-bind="foreach: properties">
               <tr>
                 <td><input type="text" class="span4 required propKey" data-bind="value: name, uniqueName: false" /></td>
-                <td><input type="text" class="span4 required pathChooserKo" data-bind="fileChooser: $data, value: value, uniqueName: false" /></td>
+                <td><input type="text" class="span4 required" data-bind="fileChooser: $data, value: value, uniqueName: false" /></td>
                 <td><a class="btn" href="#" data-bind="click: $root.removeProp">${ _('Delete') }</a></td>
               </tr>
             </tbody>
@@ -192,7 +192,7 @@ ${ layout.menubar(section='workflows') }
               <table class="table-condensed designTable" data-bind="visible: files().length > 0">
                 <tbody data-bind="foreach: files">
                   <tr>
-                    <td><input type="text" class="input span5 required pathChooserKo"
+                    <td><input type="text" class="span5 required"
                             data-bind="fileChooser: $data, value: name, uniqueName: false" />
                     </td>
                     <td><a class="btn" href="#" data-bind="click: $root.removeFile">${ _('Delete') }</a></td>
@@ -219,7 +219,7 @@ ${ layout.menubar(section='workflows') }
             <tbody data-bind="foreach: archives">
               <tr>
                 <td>
-                  <input type="text" class="input span5 required pathChooserKo"
+                  <input type="text" class="span5 required"
                       data-bind="fileChooser: $data, value: name, uniqueName: false" />
                 </td>
                 <td><a class="btn" href="#" data-bind="click: $root.removeArchive">${ _('Delete') }</a></td>
@@ -283,9 +283,11 @@ ${ layout.menubar(section='workflows') }
   .designTable {
     margin-left:0;
   }
+
   .designTable th, .designTable td {
     padding-left: 0;
   }
+
   .designTable th {
     text-align:left;
   }
@@ -362,7 +364,6 @@ ${ layout.menubar(section='workflows') }
             self.params.push({ value: "", type: "env-var" });
         };
 
-
         self.removeParam = function(val) {
             self.params.remove(val);
         };
@@ -453,11 +454,12 @@ ${ layout.menubar(section='workflows') }
         $("#fileChooserModal").jHueFileChooser({
           onFileChoose: function(filePath) {
               inputElement.val(filePath);
+              inputElement.change();
               $("#chooseFile").modal("hide");
           },
           createFolder: false
         });
-      $("#chooseFile").modal("show");
+        $("#chooseFile").modal("show");
       })
     }
 

+ 10 - 3
apps/oozie/src/oozie/templates/utils.inc.mako

@@ -45,14 +45,21 @@
 
 <%def name="job_duration(job)">
   <%
-    return ( time.mktime(job.endTime) - time.mktime(job.startTime) ) * 1000
+    if job.endTime and job.startTime:
+      return ( time.mktime(job.endTime) - time.mktime(job.startTime) ) * 1000
+    else:
+      return None
   %>
 </%def>
 
 
 <%def name="format_job_duration(job)">
   <%
-    return format_duration_in_millis(job_duration(job))
+    duration = job_duration(job)
+    if duration is not None:
+      return format_duration_in_millis(duration)
+    else:
+      return None
   %>
 </%def>
 
@@ -163,7 +170,7 @@
       <div class="controls">
         ${ field }
         % if field.errors:
-          <span class="help-inline">${ unicode(field.errors) | h }</span>
+          <span class="help-inline">${ unicode(field.errors) }</span>
         % endif
       </div>
     </div>

+ 30 - 0
apps/oozie/src/oozie/tests.py

@@ -431,6 +431,36 @@ class TestEditor:
       finish()
 
 
+  def test_workflow_action(self):
+    response = self.c.get(reverse('oozie:new_action', args=[self.wf.id, 'java', self.wf.start.id]))
+    assert_true('action="/oozie/new_action/%d/java/' % self.wf.id in response.content, response.content)
+
+    data = {u'job_xml': [u'job.xml'], u'files': [u'["my_file"]'], u'name': [u'TestActionSleep'],
+            u'jar_path': [u'/user/hue/oozie/examples/lib/hadoop-examples.jar'], u'java_opts': [u'-d64'],
+            u'args': [u'${n}'],
+            u'job_properties': [u'[{"name":"mapred.job.queue.name","value":"prod"}]'],
+            u'prepares': [u'[{"value":"${input}","type":"delete"},{"value":"${input}","type":"mkdir"}]'],
+            u'params': [u'[]'], u'archives': [u'["my_archive.zip"]'],
+            u'main_class': [u'org.apache.hadoop.examples.SleepJob'],
+            u'description': [u'An action that sleeps']}
+
+    response = self.c.post(reverse('oozie:new_action', args=[self.wf.id, 'java', self.wf.start.id]), data, follow=True)
+    assert_true('action="/oozie/edit_workflow/%d"' % self.wf.id in response.content, response.content)
+
+    action = Node.objects.get(name='TestActionSleep').get_full_node()
+    for field, value in data.iteritems():
+      if field != 'params':
+        assert_equal(value[0], getattr(action, field))
+
+    response = self.c.post(reverse('oozie:edit_action', args=[action.id]), data, follow=True)
+    assert_true('action="/oozie/edit_workflow/%d"' % self.wf.id in response.content, response.content)
+
+    action = Node.objects.get(name='TestActionSleep').get_full_node()
+    for field, value in data.iteritems():
+      if field != 'params':
+        assert_equal(value[0], getattr(action, field))
+
+
   def test_workflow_flatten_list(self):
     assert_equal('[<Start: start>, <Mapreduce: action-name-1>, <Mapreduce: action-name-2>, <Mapreduce: action-name-3>, '
                  '<Kill: kill>, <End: end>]',

+ 6 - 6
apps/oozie/src/oozie/views/editor.py

@@ -392,9 +392,9 @@ def new_action(request, workflow, node_type, parent_action_id):
 
   return render('editor/edit_workflow_action.mako', request, {
       'workflow': workflow,
-      'job_properties': 'job_properties' in action_form and extract_field_data(action_form['job_properties']) or '[]',
-      'files': 'files' in action_form and extract_field_data(action_form['files']) or '[]',
-      'archives': 'archives' in action_form and extract_field_data(action_form['archives']) or '[]',
+      'job_properties': 'job_properties' in action_form.fields and extract_field_data(action_form['job_properties']) or '[]',
+      'files': 'files' in action_form.fields and extract_field_data(action_form['files']) or '[]',
+      'archives': 'archives' in action_form.fields and extract_field_data(action_form['archives']) or '[]',
       'params': 'params' in action_form.fields and extract_field_data(action_form['params']) or '[]',
       'prepares': 'prepares' in action_form.fields and extract_field_data(action_form['prepares']) or '[]',
       'action_form': action_form,
@@ -421,9 +421,9 @@ def edit_action(request, action):
 
   return render('editor/edit_workflow_action.mako', request, {
     'workflow': action.workflow,
-    'job_properties': 'job_properties' in action_form and extract_field_data(action_form['job_properties']) or '[]',
-    'files': 'files' in action_form and extract_field_data(action_form['files']) or '[]',
-    'archives': 'archives' in action_form and extract_field_data(action_form['archives']) or '[]',
+    'job_properties': 'job_properties' in action_form.fields and extract_field_data(action_form['job_properties']) or '[]',
+    'files': 'files' in action_form.fields and extract_field_data(action_form['files']) or '[]',
+    'archives': 'archives' in action_form.fields and extract_field_data(action_form['archives']) or '[]',
     'params': 'params' in action_form.fields and extract_field_data(action_form['params']) or '[]',
     'prepares': 'prepares' in action_form.fields and extract_field_data(action_form['prepares']) or '[]',
     'action_form': action_form,