github.com/fnproject/cli@v0.0.0-20240508150455-e5d88bd86117/commands/migrate.go (about)

     1  /*
     2   * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package commands
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"io/ioutil"
    23  	"os"
    24  	"path/filepath"
    25  
    26  	"github.com/fnproject/cli/common"
    27  	"github.com/fnproject/cli/config"
    28  	yamltojson "github.com/ghodss/yaml"
    29  	"github.com/mitchellh/mapstructure"
    30  	"github.com/urfave/cli"
    31  	yaml "gopkg.in/yaml.v2"
    32  )
    33  
    34  const (
    35  	MigrateSuccessMessage = "Successfully migrated func.yaml and created a back up func.yaml.bak"
    36  	MigrateFailureMessage = "you have an up to date func.yaml file and do not need to migrate"
    37  )
    38  
    39  type migrateFnCmd struct {
    40  	newFF *common.FuncFileV20180708
    41  }
    42  
    43  func MigrateCommand() cli.Command {
    44  	m := &migrateFnCmd{newFF: &common.FuncFileV20180708{}}
    45  
    46  	return cli.Command{
    47  		Name:        "migrate",
    48  		Usage:       "\tMigrate a local func.yaml file to the latest version",
    49  		Category:    "DEVELOPMENT COMMANDS",
    50  		Aliases:     []string{"m"},
    51  		Description: "This command will detect the version of a func.yaml file and update it to match the latest version supported by the Fn CLI.\n\tAny old or unsupported attributes will be removed, and any new ones may be added.\n\tThe current func.yaml will be renamed to func.yaml.bak and a new func.yaml created.",
    52  		Action:      m.migrate,
    53  	}
    54  }
    55  
    56  func (m *migrateFnCmd) migrate(c *cli.Context) error {
    57  	var err error
    58  	oldFF, err := common.ReadInFuncFile()
    59  	if err != nil {
    60  		return err
    61  	}
    62  
    63  	version := common.GetFuncYamlVersion(oldFF)
    64  	if version == common.LatestYamlVersion {
    65  		return errors.New(MigrateFailureMessage)
    66  	}
    67  
    68  	err = backUpYamlFile(oldFF)
    69  	if err != nil {
    70  		return err
    71  	}
    72  
    73  	b, err := m.creatFuncFileBytes(oldFF)
    74  	if err != nil {
    75  		return err
    76  	}
    77  
    78  	err = vaidateFuncFileSchema(b)
    79  	if err != nil {
    80  		return err
    81  	}
    82  
    83  	err = writeYamlFile(b, "func.yaml")
    84  	if err != nil {
    85  		return err
    86  	}
    87  
    88  	fmt.Println(MigrateSuccessMessage)
    89  	return nil
    90  }
    91  
    92  func backUpYamlFile(ff map[string]interface{}) error {
    93  	b, err := yaml.Marshal(ff)
    94  	if err != nil {
    95  		return err
    96  	}
    97  
    98  	return writeYamlFile(b, "func.yaml.bak")
    99  }
   100  
   101  func (m *migrateFnCmd) decodeFuncFile(oldFF map[string]interface{}) ([]byte, error) {
   102  	_ = mapstructure.Decode(oldFF, &m.newFF)
   103  	return yaml.Marshal(oldFF)
   104  }
   105  
   106  func (m *migrateFnCmd) creatFuncFileBytes(oldFF map[string]interface{}) ([]byte, error) {
   107  	b, err := m.decodeFuncFile(oldFF)
   108  	if err != nil {
   109  		return nil, err
   110  	}
   111  
   112  	err = vaidateFuncFileSchema(b)
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  
   117  	m.newFF.Schema_version = 20180708
   118  	trig := make([]common.Trigger, 1)
   119  
   120  	var trigName, trigSource string
   121  
   122  	if oldFF["name"] != nil {
   123  		trigName = oldFF["name"].(string)
   124  		trigSource = "/" + oldFF["name"].(string)
   125  	}
   126  
   127  	trigType := "http"
   128  
   129  	trig[0] = common.Trigger{
   130  		trigName,
   131  		trigType,
   132  		trigSource,
   133  	}
   134  	m.newFF.Triggers = trig
   135  
   136  	return yaml.Marshal(m.newFF)
   137  }
   138  
   139  func vaidateFuncFileSchema(b []byte) error {
   140  	jsonB, err := yamltojson.YAMLToJSON(b)
   141  	if err != nil {
   142  		return err
   143  	}
   144  
   145  	err = ioutil.WriteFile("temp.json", jsonB, config.ReadWritePerms)
   146  	if err != nil {
   147  		return err
   148  	}
   149  	defer os.Remove("temp.json")
   150  
   151  	err = common.ValidateFileAgainstSchema("temp.json", common.V20180708Schema)
   152  	if err != nil {
   153  		return err
   154  	}
   155  
   156  	return nil
   157  }
   158  
   159  func writeYamlFile(b []byte, filename string) error {
   160  	wd := common.GetWd()
   161  	fpath := filepath.Join(wd, filename)
   162  
   163  	return ioutil.WriteFile(fpath, b, config.ReadWritePerms)
   164  }