blob: 13c610ae957f5280a3df701ee4269ca6167fcaae [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 utils
import (
"crypto/sha1"
"encoding/json"
"fmt"
"os"
"github.com/apache/openwhisk-client-go/whisk"
)
/*
* The whole purpose of this utility is to create a managed annotation for managed deployment.
* Every OpenWhisk entity in the manifest file will be annotated with:
* managed:
* __OW__PROJECT__NAME: MyProject
* __OW__PROJECT_HASH: SHA1("OpenWhisk " + <size_of_manifest_file> + "\0" + <contents_of_manifest_file>)
* __OW__FILE: Absolute path of manifest file on file system
*/
const (
MANAGED = "whisk-managed"
OPENWHISK = "OpenWhisk"
NULL = "golang\000"
OW_PROJECT_NAME = "projectName"
OW_PROJECT_HASH = "projectHash"
OW_PROJECT_DEPS = "projectDeps"
OW_File = "file"
)
type ManagedAnnotation struct {
ProjectName string `json:"projectName"`
ProjectHash string `json:"projectHash"`
File string `json:"file"`
Deps whisk.KeyValueArr `json:"projectDeps"`
}
// Project Hash is generated based on the following formula:
// SHA1("OpenWhisk " + <size_of_manifest_file> + "\0" + <contents_of_manifest_file>)
// Where the text OpenWhisk is a constant prefix
// \0 is also constant and is the NULL character
// The <size_of_manifest_file> and <contents_of_manifest_file> vary depending on the manifest file
func generateProjectHash(filePath string) (string, error) {
projectHash := ""
// open file to read manifest file and find its size
file, err := os.Open(filePath)
defer file.Close()
if err != nil {
return projectHash, err
}
// run stat on the manifest file to get its size
f, err := file.Stat()
if err != nil {
return projectHash, err
}
size := f.Size()
// read the file contents
contents := make([]byte, size)
_, err = file.Read(contents)
if err != nil {
return projectHash, err
}
// combine all the hash components used to generate SHA1
hashContents := OPENWHISK + string(rune(size)) + NULL + string(contents)
// generate a new hash.Hash computing the SHA1 checksum
h := sha1.New()
h.Write([]byte(hashContents))
// Sum returns the SHA-1 checksum of the data
hash := h.Sum(nil)
// return SHA-1 checksum in hex format
projectHash = fmt.Sprintf("%x", hash)
return projectHash, nil
}
func GenerateManagedAnnotation(projectName string, filePath string) (whisk.KeyValue, error) {
projectHash, err := generateProjectHash(filePath)
managedAnnotation := whisk.KeyValue{}
if err != nil {
return managedAnnotation, err
}
m := ManagedAnnotation{
ProjectName: projectName,
ProjectHash: projectHash,
File: filePath,
Deps: make(whisk.KeyValueArr, 0),
}
ma, err := structToJson(m)
if err != nil {
return managedAnnotation, err
}
managedAnnotation = whisk.KeyValue{Key: MANAGED, Value: ma}
return managedAnnotation, nil
}
func AddDependentAnnotation(existingAnnotation map[string]interface{}, dependencyAnnotations whisk.KeyValueArr) (whisk.KeyValue, error) {
managedAnnotation := whisk.KeyValue{}
m := ManagedAnnotation{
ProjectName: existingAnnotation[OW_PROJECT_NAME].(string),
ProjectHash: existingAnnotation[OW_PROJECT_HASH].(string),
File: existingAnnotation[OW_File].(string),
Deps: dependencyAnnotations,
}
ma, err := structToJson(m)
if err != nil {
return managedAnnotation, err
}
managedAnnotation = whisk.KeyValue{Key: MANAGED, Value: ma}
return managedAnnotation, nil
}
func structToJson(m ManagedAnnotation) (map[string]interface{}, error) {
ma, err := json.Marshal(m)
if err != nil {
return nil, err
}
var a interface{}
json.Unmarshal(ma, &a)
return a.(map[string]interface{}), nil
}