1// Copyright 2016-2018 VMware, Inc. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//    http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package management
16
17import (
18	"context"
19	"io/ioutil"
20	"net/url"
21	"os"
22	"path"
23	"testing"
24
25	log "github.com/sirupsen/logrus"
26
27	"github.com/vmware/govmomi/object"
28	"github.com/vmware/govmomi/simulator"
29	"github.com/vmware/govmomi/vim25/types"
30	"github.com/vmware/vic/lib/config"
31	"github.com/vmware/vic/lib/install/data"
32	"github.com/vmware/vic/lib/install/validate"
33	"github.com/vmware/vic/pkg/errors"
34	"github.com/vmware/vic/pkg/trace"
35	"github.com/vmware/vic/pkg/vsphere/session"
36)
37
38func TestDelete(t *testing.T) {
39	log.SetLevel(log.DebugLevel)
40	trace.Logger.Level = log.DebugLevel
41	ctx := context.Background()
42	op := trace.NewOperation(ctx, "TestDelete")
43
44	for i, model := range []*simulator.Model{simulator.ESX(), simulator.VPX()} {
45		t.Logf("%d", i)
46		defer model.Remove()
47		err := model.Create()
48		if err != nil {
49			t.Fatal(err)
50		}
51
52		s := model.Service.NewServer()
53		defer s.Close()
54
55		s.URL.User = url.UserPassword("user", "pass")
56		s.URL.Path = ""
57		t.Logf("server URL: %s", s.URL)
58
59		var input *data.Data
60		if i == 0 {
61			input = getESXData(s.URL)
62		} else {
63			input = getVPXData(s.URL)
64		}
65		if err != nil {
66			t.Fatal(err)
67		}
68		installSettings := &data.InstallerData{}
69		cpu := int64(1)
70		memory := int64(1024)
71		installSettings.ApplianceSize.CPU.Limit = &cpu
72		installSettings.ApplianceSize.Memory.Limit = &memory
73		installSettings.ResourcePoolPath = path.Join(input.ComputeResourcePath, input.DisplayName)
74
75		validator, err := validate.NewValidator(ctx, input)
76		if err != nil {
77			t.Errorf("Failed to validator: %s", err)
78		}
79
80		conf, err := validator.Validate(ctx, input, false)
81		if err != nil {
82			log.Errorf("Failed to validate conf: %s", err)
83			validator.ListIssues(op)
84		}
85
86		testCreateNetwork(op, validator.Session(), conf, t)
87		createAppliance(ctx, validator.Session(), conf, installSettings, false, t)
88
89		testNewVCHFromCompute(op, input.ComputeResourcePath, input.DisplayName, validator, t)
90		//		testUpgrade(input.ComputeResourcePath, input.DisplayName, validator, installSettings, t) TODO: does not implement: CreateSnapshot_Task
91		testDeleteVCH(op, validator, conf, t)
92
93		testDeleteDatastoreFiles(op, validator, t)
94	}
95}
96
97func testUpgrade(op trace.Operation, computePath string, name string, v *validate.Validator, settings *data.InstallerData, t *testing.T) {
98	// TODO: add tests for rollback after snapshot func is added in vcsim
99	d := &Dispatcher{
100		session: v.Session(),
101		op:      op,
102		isVC:    v.Session().IsVC(),
103		force:   false,
104	}
105	vch, err := d.NewVCHFromComputePath(computePath, name, v)
106	if err != nil {
107		t.Errorf("Failed to get VCH: %s", err)
108		return
109	}
110	t.Logf("Got VCH %s, path %s", vch, path.Dir(vch.InventoryPath))
111	conf, err := d.GetVCHConfig(vch)
112	if err != nil {
113
114		t.Errorf("Failed to get vch configuration: %s", err)
115	}
116	if err := d.Configure(conf, settings, true); err != nil {
117		t.Errorf("Failed to upgrade: %s", err)
118	}
119}
120
121func createAppliance(ctx context.Context, sess *session.Session, conf *config.VirtualContainerHostConfigSpec, vConf *data.InstallerData, hasErr bool, t *testing.T) {
122	var err error
123
124	d := &Dispatcher{
125		session: sess,
126		op:      trace.FromContext(ctx, "createAppliance"),
127		isVC:    sess.IsVC(),
128		force:   false,
129	}
130
131	err = d.createPool(conf, vConf)
132	if err != nil {
133		t.Fatal(err)
134	}
135
136	err = d.createAppliance(conf, vConf)
137	if err != nil {
138		t.Fatal(err)
139	}
140}
141
142func testNewVCHFromCompute(op trace.Operation, computePath string, name string, v *validate.Validator, t *testing.T) {
143	d := &Dispatcher{
144		session: v.Session(),
145		op:      op,
146		isVC:    v.Session().IsVC(),
147		force:   false,
148	}
149	vch, err := d.NewVCHFromComputePath(computePath, name, v)
150	if err != nil {
151		t.Errorf("Failed to get VCH: %s", err)
152		return
153	}
154
155	if d.session.Cluster == nil {
156		t.Errorf("Failed to set cluster: %s", err)
157		return
158	}
159
160	t.Logf("Got VCH %s, path %s", vch, path.Dir(vch.InventoryPath))
161}
162
163func testDeleteVCH(op trace.Operation, v *validate.Validator, conf *config.VirtualContainerHostConfigSpec, t *testing.T) {
164	d := &Dispatcher{
165		session: v.Session(),
166		op:      op,
167		isVC:    v.Session().IsVC(),
168		force:   false,
169	}
170	// failed to get vm FolderName, that will eventually cause panic in simulator to delete empty datastore file
171	if err := d.DeleteVCH(conf, nil, nil); err != nil {
172		t.Errorf("Failed to get VCH: %s", err)
173		return
174	}
175	t.Logf("Successfully deleted VCH")
176	// check images directory is removed
177	dsPath := "[LocalDS_0] VIC"
178	_, err := d.lsFolder(v.Session().Datastore, dsPath)
179	if err != nil {
180		if !types.IsFileNotFound(err) {
181			t.Errorf("Failed to browse folder %s: %s", dsPath, errors.ErrorStack(err))
182		}
183		t.Logf("Images Folder is not found")
184	}
185
186	// check appliance vm is deleted
187	vm, err := d.findApplianceByID(conf)
188	if vm != nil {
189		t.Errorf("Should not found vm %s", vm.Reference())
190	}
191	if err != nil {
192		t.Errorf("Unexpected error to get appliance VM: %s", err)
193	}
194
195	// Verify that the VCH folder (if created on VC) is deleted after VCH delete.
196	folderPath := path.Join(d.session.VMFolder.InventoryPath, conf.Name)
197	vchFolder, err := d.session.Finder.Folder(d.op, folderPath)
198	if vchFolder != nil || err == nil {
199		t.Errorf("Should not have found VCH folder %q after VCH delete", folderPath)
200	}
201
202	// delete VM does not clean up resource pool after VM is removed, so resource pool could not be removed
203}
204
205func testDeleteDatastoreFiles(op trace.Operation, v *validate.Validator, t *testing.T) {
206	d := &Dispatcher{
207		session: v.Session(),
208		op:      op,
209		isVC:    v.Session().IsVC(),
210		force:   false,
211	}
212
213	ds := v.Session().Datastore
214	m := object.NewFileManager(ds.Client())
215	err := m.MakeDirectory(op, ds.Path("Test/folder/data"), v.Session().Datacenter, true)
216	if err != nil {
217		t.Errorf("Failed to create datastore dir: %s", err)
218		return
219	}
220	err = m.MakeDirectory(op, ds.Path("Test/folder/metadata"), v.Session().Datacenter, true)
221	if err != nil {
222		t.Errorf("Failed to create datastore dir: %s", err)
223		return
224	}
225	err = m.MakeDirectory(op, ds.Path("Test/folder/file"), v.Session().Datacenter, true)
226	if err != nil {
227		t.Errorf("Failed to create datastore dir: %s", err)
228		return
229	}
230
231	isVSAN := d.isVSAN(ds)
232	t.Logf("datastore is vsan: %t", isVSAN)
233
234	if err = createDatastoreFiles(d, ds, t); err != nil {
235		t.Errorf("Failed to upload file: %s", err)
236		return
237	}
238
239	fm := ds.NewFileManager(d.session.Datacenter, true)
240	if err = d.deleteFilesIteratively(fm, ds, ds.Path("Test")); err != nil {
241		t.Errorf("Failed to delete recursively: %s", err)
242	}
243
244	err = m.MakeDirectory(op, ds.Path("Test/folder/data"), v.Session().Datacenter, true)
245	if err != nil {
246		t.Errorf("Failed to create datastore dir: %s", err)
247		return
248	}
249
250	if err = createDatastoreFiles(d, ds, t); err != nil {
251		t.Errorf("Failed to upload file: %s", err)
252		return
253	}
254
255	if _, err = d.deleteDatastoreFiles(ds, "Test", true); err != nil {
256		t.Errorf("Failed to delete recursively: %s", err)
257	}
258}
259
260func createDatastoreFiles(d *Dispatcher, ds *object.Datastore, t *testing.T) error {
261	tmpfile, err := ioutil.TempFile("", "tempDatastoreFile.vmdk")
262	if err != nil {
263		t.Errorf("Failed to create file: %s", err)
264		return err
265	}
266
267	defer os.Remove(tmpfile.Name()) // clean up
268
269	if err = ds.UploadFile(d.op, tmpfile.Name(), "Test/folder/data/temp.vmdk", nil); err != nil {
270		t.Errorf("Failed to upload file %q: %s", "Test/folder/data/temp.vmdk", err)
271		return err
272	}
273	if err = ds.UploadFile(d.op, tmpfile.Name(), "Test/folder/tempMetadata", nil); err != nil {
274		t.Errorf("Failed to upload file %q: %s", "Test/folder/tempMetadata", err)
275		return err
276	}
277	return nil
278}
279