1package state
2
3import (
4	biagentclient "github.com/cloudfoundry/bosh-agent/agentclient"
5	biblobstore "github.com/cloudfoundry/bosh-cli/blobstore"
6	birelpkg "github.com/cloudfoundry/bosh-cli/release/pkg"
7	bistatepkg "github.com/cloudfoundry/bosh-cli/state/pkg"
8	bosherr "github.com/cloudfoundry/bosh-utils/errors"
9)
10
11type remotePackageCompiler struct {
12	blobstore   biblobstore.Blobstore
13	agentClient biagentclient.AgentClient
14	packageRepo bistatepkg.CompiledPackageRepo
15}
16
17func NewRemotePackageCompiler(
18	blobstore biblobstore.Blobstore,
19	agentClient biagentclient.AgentClient,
20	packageRepo bistatepkg.CompiledPackageRepo,
21) bistatepkg.Compiler {
22	return &remotePackageCompiler{
23		blobstore:   blobstore,
24		agentClient: agentClient,
25		packageRepo: packageRepo,
26	}
27}
28
29func (c *remotePackageCompiler) Compile(pkg birelpkg.Compilable) (bistatepkg.CompiledPackageRecord, bool, error) {
30	var record bistatepkg.CompiledPackageRecord
31
32	blobID, err := c.blobstore.Add(pkg.ArchivePath())
33	if err != nil {
34		return bistatepkg.CompiledPackageRecord{}, false, bosherr.WrapErrorf(err, "Adding release package archive '%s' to blobstore", pkg.ArchivePath())
35	}
36
37	packageSource := biagentclient.BlobRef{
38		Name:        pkg.Name(),
39		Version:     pkg.Fingerprint(),
40		SHA1:        pkg.ArchiveDigest(),
41		BlobstoreID: blobID,
42	}
43
44	var isAlreadyCompiled bool
45
46	if !pkg.IsCompiled() {
47		// Resolve dependencies from map of previously compiled packages.
48		// Only install the package's immediate dependencies.
49		packageDependencies := make([]biagentclient.BlobRef, len(pkg.Deps()), len(pkg.Deps()))
50
51		for i, pkgDep := range pkg.Deps() {
52			compiledPackageRecord, found, err := c.packageRepo.Find(pkgDep)
53			if err != nil {
54				return record, false, bosherr.WrapErrorf(
55					err,
56					"Finding compiled package '%s/%s' as pkgDep for '%s/%s'",
57					pkgDep.Name(),
58					pkgDep.Fingerprint(),
59					pkg.Name(),
60					pkg.Fingerprint(),
61				)
62			}
63			if !found {
64				return record, false, bosherr.Errorf(
65					"Remote compilation failure: Package '%s/%s' requires package '%s/%s', but it has not been compiled",
66					pkg.Name(),
67					pkg.Fingerprint(),
68					pkgDep.Name(),
69					pkgDep.Fingerprint(),
70				)
71			}
72			packageDependencies[i] = biagentclient.BlobRef{
73				Name:        pkgDep.Name(),
74				Version:     pkgDep.Fingerprint(),
75				BlobstoreID: compiledPackageRecord.BlobID,
76				SHA1:        compiledPackageRecord.BlobSHA1,
77			}
78		}
79
80		compiledPackageRef, err := c.agentClient.CompilePackage(packageSource, packageDependencies)
81		if err != nil {
82			return record, false, bosherr.WrapErrorf(err, "Remotely compiling package '%s' with the agent", pkg.Name())
83		}
84
85		record = bistatepkg.CompiledPackageRecord{
86			BlobID:   compiledPackageRef.BlobstoreID,
87			BlobSHA1: compiledPackageRef.SHA1,
88		}
89	} else {
90		isAlreadyCompiled = true
91
92		record = bistatepkg.CompiledPackageRecord{
93			BlobID:   blobID,
94			BlobSHA1: pkg.ArchiveDigest(),
95		}
96	}
97
98	err = c.packageRepo.Save(pkg, record)
99	if err != nil {
100		return record, isAlreadyCompiled, bosherr.WrapErrorf(err, "Saving compiled package record '%#v' of package '%#v'", record, pkg)
101	}
102
103	return record, isAlreadyCompiled, nil
104}
105