feat(apps): add migration '' -> 'ansible'

This commit is contained in:
Denis Gukov 2024-07-10 00:26:12 +05:00
parent c4916703f0
commit 26c3479f8a
7 changed files with 204 additions and 35 deletions

View File

@ -70,6 +70,7 @@ func GetMigrations() []Migration {
{Version: "2.9.100"}, {Version: "2.9.100"},
{Version: "2.10.12"}, {Version: "2.10.12"},
{Version: "2.10.15"}, {Version: "2.10.15"},
{Version: "2.10.16"},
} }
} }

View File

@ -43,6 +43,8 @@ func (d *BoltDb) ApplyMigration(m db.Migration) (err error) {
err = migration_2_8_91{migration{d.db}}.Apply() err = migration_2_8_91{migration{d.db}}.Apply()
case "2.10.12": case "2.10.12":
err = migration_2_10_12{migration{d.db}}.Apply() err = migration_2_10_12{migration{d.db}}.Apply()
case "2.10.16":
err = migration_2_10_16{migration{d.db}}.Apply()
} }
if err != nil { if err != nil {

View File

@ -0,0 +1,38 @@
package bolt
type migration_2_10_16 struct {
migration
}
func (d migration_2_10_16) Apply() (err error) {
projectIDs, err := d.getProjectIDs()
if err != nil {
return
}
templates := make(map[string]map[string]map[string]interface{})
for _, projectID := range projectIDs {
var err2 error
templates[projectID], err2 = d.getObjects(projectID, "template")
if err2 != nil {
return err2
}
}
for projectID, projectTemplates := range templates {
for repoID, tpl := range projectTemplates {
if tpl["app"] != nil && tpl["app"] != "" {
continue
}
tpl["app"] = "ansible"
err = d.setObject(projectID, "template", repoID, tpl)
if err != nil {
return err
}
}
}
return
}

View File

@ -0,0 +1,88 @@
package bolt
import (
"encoding/json"
"go.etcd.io/bbolt"
"testing"
)
func TestMigration_2_10_16_Apply(t *testing.T) {
store := CreateTestStore()
err := store.db.Update(func(tx *bbolt.Tx) error {
b, err := tx.CreateBucketIfNotExists([]byte("project"))
if err != nil {
return err
}
err = b.Put([]byte("0000000001"), []byte("{}"))
if err != nil {
return err
}
r, err := tx.CreateBucketIfNotExists([]byte("project__template_0000000001"))
if err != nil {
return err
}
err = r.Put([]byte("0000000001"),
[]byte("{\"id\":\"1\",\"project_id\":\"1\"}"))
return err
})
if err != nil {
t.Fatal(err)
}
err = migration_2_10_16{migration{store.db}}.Apply()
if err != nil {
t.Fatal(err)
}
var repo map[string]interface{}
err = store.db.View(func(tx *bbolt.Tx) error {
b := tx.Bucket([]byte("project__template_0000000001"))
str := string(b.Get([]byte("0000000001")))
return json.Unmarshal([]byte(str), &repo)
})
if err != nil {
t.Fatal(err)
}
if repo["app"] == nil {
t.Fatal("app must be set")
}
if repo["app"].(string) != "ansible" {
t.Fatal("invalid app: " + repo["app"].(string))
}
if repo["alias"] != nil {
t.Fatal("alias must be deleted")
}
}
func TestMigration_2_10_16_Apply2(t *testing.T) {
store := CreateTestStore()
err := store.db.Update(func(tx *bbolt.Tx) error {
b, err := tx.CreateBucketIfNotExists([]byte("project"))
if err != nil {
return err
}
err = b.Put([]byte("0000000001"), []byte("{}"))
return err
})
if err != nil {
t.Fatal(err)
}
err = migration_2_10_16{migration{store.db}}.Apply()
if err != nil {
t.Fatal(err)
}
}

View File

@ -0,0 +1 @@
update `project__template` set `app` = 'ansible' where `app` = '';

View File

@ -11,8 +11,7 @@
@save="onSave" @save="onSave"
> >
<template v-slot:form="{ onSave, onError, needSave, needReset }"> <template v-slot:form="{ onSave, onError, needSave, needReset }">
<TerraformTemplateForm <TemplateForm
v-if="['terraform', 'tofu'].includes(itemApp)"
:project-id="projectId" :project-id="projectId"
:item-id="itemId" :item-id="itemId"
@save="onSave" @save="onSave"
@ -21,27 +20,7 @@
:need-reset="needReset" :need-reset="needReset"
:source-item-id="sourceItemId" :source-item-id="sourceItemId"
:app="itemApp" :app="itemApp"
/> :fields="fields"
<TemplateForm
v-else-if="['', 'ansible'].includes(itemApp)"
:project-id="projectId"
:item-id="itemId"
@save="onSave"
@error="onError"
:need-save="needSave"
:need-reset="needReset"
:source-item-id="sourceItemId"
/>
<ShellTemplateForm
v-else
:project-id="projectId"
:item-id="itemId"
@save="onSave"
@error="onError"
:need-save="needSave"
:need-reset="needReset"
:source-item-id="sourceItemId"
:app-id="itemApp"
/> />
</template> </template>
</EditDialog> </EditDialog>
@ -53,16 +32,57 @@
<script> <script>
import TerraformTemplateForm from './TerraformTemplateForm.vue';
import ShellTemplateForm from './ShellTemplateForm.vue';
import TemplateForm from './TemplateForm.vue'; import TemplateForm from './TemplateForm.vue';
import EditDialog from './EditDialog.vue'; import EditDialog from './EditDialog.vue';
import AppsMixin from './AppsMixin'; import AppsMixin from './AppsMixin';
const ANSIBLE_FIELDS = {
playbook: {
label: 'playbookFilename',
},
inventory: {
label: 'inventory2',
},
repository: {
label: 'repository',
},
environment: {
label: 'environment3',
},
vault: {
label: 'vaultPassword2',
},
};
const TERRAFORM_FIELDS = {
...ANSIBLE_FIELDS,
playbook: {
label: 'Subdirectory path (Optional)',
},
inventory: {
label: 'Default Workspace',
},
vault: undefined,
};
const UNKNOWN_APP_FIELDS = {
...ANSIBLE_FIELDS,
playbook: {
label: 'Script Filename',
},
inventory: undefined,
vault: undefined,
};
const APP_FIELDS = {
'': ANSIBLE_FIELDS,
ansible: ANSIBLE_FIELDS,
terraform: TERRAFORM_FIELDS,
tofu: TERRAFORM_FIELDS,
};
export default { export default {
components: { components: {
ShellTemplateForm,
TerraformTemplateForm,
TemplateForm, TemplateForm,
EditDialog, EditDialog,
}, },
@ -83,6 +103,12 @@ export default {
}; };
}, },
computed: {
fields() {
return APP_FIELDS[this.itemApp] || UNKNOWN_APP_FIELDS;
},
},
watch: { watch: {
async dialog(val) { async dialog(val) {
this.$emit('input', val); this.$emit('input', val);

View File

@ -148,18 +148,19 @@
<v-text-field <v-text-field
v-model="item.playbook" v-model="item.playbook"
:label="$t('playbookFilename')" :label="fieldLabel('playbook')"
:rules="[v => !!v || $t('playbook_filename_required')]" :rules="[v => !!v || $t('playbook_filename_required')]"
outlined outlined
dense dense
required required
:disabled="formSaving" :disabled="formSaving"
:placeholder="$t('exampleSiteyml')" :placeholder="$t('exampleSiteyml')"
v-if="needField('playbook')"
></v-text-field> ></v-text-field>
<v-select <v-select
v-model="item.inventory_id" v-model="item.inventory_id"
:label="$t('inventory2')" :label="fieldLabel('inventory')"
:items="inventory" :items="inventory"
item-value="id" item-value="id"
item-text="name" item-text="name"
@ -167,11 +168,12 @@
dense dense
required required
:disabled="formSaving" :disabled="formSaving"
v-if="needField('inventory')"
></v-select> ></v-select>
<v-select <v-select
v-model="item.repository_id" v-model="item.repository_id"
:label="$t('repository') + ' *'" :label="fieldLabel('repository')"
:items="repositories" :items="repositories"
item-value="id" item-value="id"
item-text="name" item-text="name"
@ -180,11 +182,12 @@
dense dense
required required
:disabled="formSaving" :disabled="formSaving"
v-if="needField('repository')"
></v-select> ></v-select>
<v-select <v-select
v-model="item.environment_id" v-model="item.environment_id"
:label="$t('environment3')" :label="fieldLabel('environment')"
:items="environment" :items="environment"
item-value="id" item-value="id"
item-text="name" item-text="name"
@ -193,12 +196,13 @@
dense dense
required required
:disabled="formSaving" :disabled="formSaving"
v-if="needField('environment')"
></v-select> ></v-select>
<v-select <v-select
v-if="itemTypeIndex === 0" v-if="itemTypeIndex === 0 && needField('vault')"
v-model="item.vault_key_id" v-model="item.vault_key_id"
:label="$t('vaultPassword')" :label="fieldLabel('vault')"
clearable clearable
:items="loginPasswordKeys" :items="loginPasswordKeys"
item-value="id" item-value="id"
@ -212,9 +216,9 @@
<v-col cols="12" md="6" class="pb-0"> <v-col cols="12" md="6" class="pb-0">
<v-select <v-select
v-if="itemTypeIndex > 0" v-if="itemTypeIndex > 0 && needField('vault')"
v-model="item.vault_key_id" v-model="item.vault_key_id"
:label="$t('vaultPassword2')" :label="fieldLabel('vault')"
clearable clearable
:items="loginPasswordKeys" :items="loginPasswordKeys"
item-value="id" item-value="id"
@ -336,6 +340,7 @@ export default {
props: { props: {
sourceItemId: Number, sourceItemId: Number,
fields: Array,
}, },
data() { data() {
@ -428,6 +433,14 @@ export default {
}, },
methods: { methods: {
fieldLabel(f) {
return this.$t(this.fields[f]);
},
needField(f) {
return this.fields[f] != null;
},
setSurveyVars(v) { setSurveyVars(v) {
this.item.survey_vars = v; this.item.survey_vars = v;
}, },