blob: 661a2b58256b7d4b48dccabe0c3afbb5ed32fd3d [file] [log] [blame]
// Copyright 2014 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package pkg
import (
"encoding/json"
"io"
"go.chromium.org/luci/cipd/common/cipderr"
"go.chromium.org/luci/common/errors"
)
const (
// ServiceDir is a name of the directory inside the package zip file reserved
// for cipd stuff.
ServiceDir = ".cipdpkg"
// ManifestName is a full name of the manifest file inside the package.
ManifestName = ServiceDir + "/manifest.json"
// ManifestFormatVersion is a version to write to the manifest file.
ManifestFormatVersion = "1.1"
)
// Manifest defines structure of manifest.json file.
type Manifest struct {
FormatVersion string `json:"format_version"`
PackageName string `json:"package_name"`
VersionFile string `json:"version_file,omitempty"` // where to put JSON with info about deployed package
InstallMode InstallMode `json:"install_mode,omitempty"` // how to install: "copy" or "symlink"
// The following fields are present only in deployed manifests:
ActualInstallMode InstallMode `json:"actual_install_mode,omitempty"` // how this specific package was actually installed
Files []FileInfo `json:"files,omitempty"`
}
// FileInfo describes a file that was extracted from a CIPD package.
//
// It is derived (based on zip info headers and actual contents of the files) by
// ExtractFiles when it unpacks the CIPD package. FileInfo structs are *not*
// stored in an explicit form in manifest.json inside the package. They are
// present only in manifest.json files on disk, representing already unpacked
// packages.
type FileInfo struct {
// Name is slash separated file path relative to a package root.
Name string `json:"name"`
// Size is a size of the file. 0 for symlinks.
Size uint64 `json:"size"`
// Executable is true if the file is executable.
//
// Only used for Linux\Mac archives. False for symlinks.
Executable bool `json:"executable,omitempty"`
// Writable is true if the file is user-writable.
Writable bool `json:"writable,omitempty"`
// ModTime is Unix timestamp with modification time of the file as it is set
// inside CIPD package.
//
// May be 0 if the package was built without preserving the modification
// times.
ModTime int64 `json:"modtime,omitempty"`
// WinAttrs is a string representation of extra windows file attributes.
//
// Only used for Win archives.
WinAttrs string `json:"win_attrs,omitempty"`
// Symlink is a path the symlink points to or "" if the file is not a symlink.
Symlink string `json:"symlink,omitempty"`
// Hash of the file body in the same format as used for instance IDs (i.e. a
// base64 string that encodes both the algorithm and the digest).
//
// See common.ObjectRefToInstanceID for more info. Uses the best algorithm
// available at the time of the package unpacking.
//
// Empty for symlink files. May also be empty in older manifests.
Hash string `json:"hash,omitempty"`
}
// VersionFile describes JSON file with package version information that's
// deployed to a path specified in 'version_file' attribute of the manifest.
type VersionFile struct {
PackageName string `json:"package_name"`
InstanceID string `json:"instance_id"`
}
// ReadManifest reads and decodes manifest JSON from io.Reader.
func ReadManifest(r io.Reader) (Manifest, error) {
blob, err := io.ReadAll(r)
if err != nil {
return Manifest{}, errors.Annotate(err, "reading manifest file").Tag(cipderr.IO).Err()
}
var manifest Manifest
if err := json.Unmarshal(blob, &manifest); err != nil {
return Manifest{}, errors.Annotate(err, "parsing manifest").Tag(cipderr.BadArgument).Err()
}
return manifest, nil
}
// WriteManifest encodes and writes manifest JSON to io.Writer.
func WriteManifest(m *Manifest, w io.Writer) error {
data, err := json.MarshalIndent(m, "", " ")
if err != nil {
return errors.Annotate(err, "serializing manifest").Tag(cipderr.BadArgument).Err()
}
if _, err = w.Write(data); err != nil {
return errors.Annotate(err, "writing manifest file").Tag(cipderr.IO).Err()
}
return nil
}