mirror of
https://github.com/containers/podman.git
synced 2025-06-24 03:08:13 +08:00
Bindings refactor
this is step one of refactoring our golang binaries. we will no be using structs to pass optional options. required options will still arguments to the binding itself. the structs then have a generator to create helper functions which should then be added to the git repo. Signed-off-by: baude <bbaude@redhat.com>
This commit is contained in:
234
pkg/bindings/generator/generator.go
Normal file
234
pkg/bindings/generator/generator.go
Normal file
@ -0,0 +1,234 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
)
|
||||
|
||||
var bodyTmpl = `package {{.PackageName}}
|
||||
|
||||
import (
|
||||
{{range $import := .Imports}} {{$import}}
|
||||
{{end}}
|
||||
|
||||
)
|
||||
|
||||
/*
|
||||
This file is generated automatically by go generate. Do not edit.
|
||||
|
||||
Created {{.Date}}
|
||||
*/
|
||||
|
||||
// Changed
|
||||
func (o *{{.StructName}}) Changed(fieldName string) bool {
|
||||
r := reflect.ValueOf(o)
|
||||
value := reflect.Indirect(r).FieldByName(fieldName)
|
||||
return !value.IsNil()
|
||||
}
|
||||
|
||||
// ToParams
|
||||
func (o *{{.StructName}}) ToParams() (url.Values, error) {
|
||||
params := url.Values{}
|
||||
if o == nil {
|
||||
return params, nil
|
||||
}
|
||||
json := jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
s := reflect.ValueOf(o)
|
||||
if reflect.Ptr == s.Kind() {
|
||||
s = s.Elem()
|
||||
}
|
||||
sType := s.Type()
|
||||
for i := 0; i < s.NumField(); i++ {
|
||||
fieldName := sType.Field(i).Name
|
||||
if !o.Changed(fieldName) {
|
||||
continue
|
||||
}
|
||||
f := s.Field(i)
|
||||
if reflect.Ptr == f.Kind() {
|
||||
f = f.Elem()
|
||||
}
|
||||
switch f.Kind() {
|
||||
case reflect.Bool:
|
||||
params.Set(fieldName, strconv.FormatBool(f.Bool()))
|
||||
case reflect.String:
|
||||
params.Set(fieldName, f.String())
|
||||
case reflect.Int, reflect.Int64:
|
||||
// f.Int() is always an int64
|
||||
params.Set(fieldName, strconv.FormatInt(f.Int(), 10))
|
||||
case reflect.Slice:
|
||||
typ := reflect.TypeOf(f.Interface()).Elem()
|
||||
slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap())
|
||||
switch typ.Kind() {
|
||||
case reflect.String:
|
||||
s, ok := slice.Interface().([]string)
|
||||
if !ok {
|
||||
return nil, errors.New("failed to convert to string slice")
|
||||
}
|
||||
for _, val := range s {
|
||||
params.Add(fieldName, val)
|
||||
}
|
||||
default:
|
||||
return nil, errors.Errorf("unknown slice type %s", f.Kind().String())
|
||||
}
|
||||
case reflect.Map:
|
||||
lowerCaseKeys := make(map[string][]string)
|
||||
// I dont know if this code is needed anymore, TBD
|
||||
// for k, v := range filters {
|
||||
// lowerCaseKeys[strings.ToLower(k)] = v
|
||||
// }
|
||||
s, err := json.MarshalToString(lowerCaseKeys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params.Set(fieldName, s)
|
||||
default:
|
||||
return nil, errors.Errorf("unknown type %s", f.Kind().String())
|
||||
}
|
||||
}
|
||||
return params, nil
|
||||
}
|
||||
`
|
||||
|
||||
var fieldTmpl = `
|
||||
// With{{.Name}}
|
||||
func(o *{{.StructName}}) With{{.Name}}(value {{.Type}}) *{{.StructName}} {
|
||||
v := &value
|
||||
o.{{.Name}} = v
|
||||
return o
|
||||
}
|
||||
`
|
||||
|
||||
type fieldStruct struct {
|
||||
Name string
|
||||
StructName string
|
||||
Type string
|
||||
}
|
||||
|
||||
func main() {
|
||||
var (
|
||||
closed bool
|
||||
fieldStructs []fieldStruct
|
||||
structNode ast.Node
|
||||
)
|
||||
srcFile := os.Getenv("GOFILE")
|
||||
pkg := os.Getenv("GOPACKAGE")
|
||||
inputStructName := os.Args[1]
|
||||
b, err := ioutil.ReadFile(srcFile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fset := token.NewFileSet() // positions are relative to fset
|
||||
f, err := parser.ParseFile(fset, "", b, parser.ParseComments)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// always add reflect
|
||||
imports := []string{"\"reflect\""}
|
||||
for _, imp := range f.Imports {
|
||||
imports = append(imports, imp.Path.Value)
|
||||
}
|
||||
|
||||
out, err := os.Create(strings.ToLower(inputStructName) + "_" + srcFile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer func() {
|
||||
if !closed {
|
||||
out.Close()
|
||||
}
|
||||
}()
|
||||
bodyStruct := struct {
|
||||
PackageName string
|
||||
Imports []string
|
||||
Date string
|
||||
StructName string
|
||||
}{
|
||||
PackageName: pkg,
|
||||
Imports: imports,
|
||||
Date: time.Now().String(),
|
||||
StructName: inputStructName,
|
||||
}
|
||||
|
||||
body := template.Must(template.New("body").Parse(bodyTmpl))
|
||||
fields := template.Must(template.New("fields").Parse(fieldTmpl))
|
||||
ast.Inspect(f, func(n ast.Node) bool {
|
||||
ref, refOK := n.(*ast.TypeSpec)
|
||||
if refOK {
|
||||
if ref.Name.Name == inputStructName {
|
||||
structNode = n
|
||||
x := ref.Type.(*ast.StructType)
|
||||
for _, field := range x.Fields.List {
|
||||
var (
|
||||
name string
|
||||
)
|
||||
typeExpr := field.Type
|
||||
start := typeExpr.Pos() - 1
|
||||
end := typeExpr.End() - 1
|
||||
fieldType := strings.Replace(string(b[start:end]), "*", "", 1)
|
||||
if len(field.Names) > 0 {
|
||||
name = field.Names[0].Name
|
||||
if len(name) < 1 {
|
||||
panic(errors.New("bad name"))
|
||||
}
|
||||
}
|
||||
fStruct := fieldStruct{
|
||||
Name: name,
|
||||
StructName: inputStructName,
|
||||
Type: fieldType,
|
||||
}
|
||||
fieldStructs = append(fieldStructs, fStruct)
|
||||
} // for
|
||||
|
||||
// create the body
|
||||
if err := body.Execute(out, bodyStruct); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// create with func from the struct fields
|
||||
for _, fs := range fieldStructs {
|
||||
if err := fields.Execute(out, fs); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// close out file
|
||||
if err := out.Close(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
closed = true
|
||||
|
||||
// go fmt file
|
||||
gofmt := exec.Command("gofmt", "-w", "-s", out.Name())
|
||||
gofmt.Stderr = os.Stdout
|
||||
if err := gofmt.Run(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// go import file
|
||||
goimport := exec.Command("goimports", "-w", out.Name())
|
||||
goimport.Stderr = os.Stdout
|
||||
if err := goimport.Run(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
93
pkg/bindings/images/removeoptions_types.go
Normal file
93
pkg/bindings/images/removeoptions_types.go
Normal file
@ -0,0 +1,93 @@
|
||||
package images
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
/*
|
||||
This file is generated automatically by go generate. Do not edit.
|
||||
|
||||
Created 2020-12-10 12:51:06.090426622 -0600 CST m=+0.000133169
|
||||
*/
|
||||
|
||||
// Changed
|
||||
func (o *RemoveOptions) Changed(fieldName string) bool {
|
||||
r := reflect.ValueOf(o)
|
||||
value := reflect.Indirect(r).FieldByName(fieldName)
|
||||
return !value.IsNil()
|
||||
}
|
||||
|
||||
// ToParams
|
||||
func (o *RemoveOptions) ToParams() (url.Values, error) {
|
||||
params := url.Values{}
|
||||
if o == nil {
|
||||
return params, nil
|
||||
}
|
||||
json := jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
s := reflect.ValueOf(o)
|
||||
if reflect.Ptr == s.Kind() {
|
||||
s = s.Elem()
|
||||
}
|
||||
sType := s.Type()
|
||||
for i := 0; i < s.NumField(); i++ {
|
||||
fieldName := sType.Field(i).Name
|
||||
if !o.Changed(fieldName) {
|
||||
continue
|
||||
}
|
||||
f := s.Field(i)
|
||||
if reflect.Ptr == f.Kind() {
|
||||
f = f.Elem()
|
||||
}
|
||||
switch f.Kind() {
|
||||
case reflect.Bool:
|
||||
params.Set(fieldName, strconv.FormatBool(f.Bool()))
|
||||
case reflect.String:
|
||||
params.Set(fieldName, f.String())
|
||||
case reflect.Int, reflect.Int64:
|
||||
// f.Int() is always an int64
|
||||
params.Set(fieldName, strconv.FormatInt(f.Int(), 10))
|
||||
case reflect.Slice:
|
||||
typ := reflect.TypeOf(f.Interface()).Elem()
|
||||
slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap())
|
||||
switch typ.Kind() {
|
||||
case reflect.String:
|
||||
s, ok := slice.Interface().([]string)
|
||||
if !ok {
|
||||
return nil, errors.New("failed to convert to string slice")
|
||||
}
|
||||
for _, val := range s {
|
||||
params.Add(fieldName, val)
|
||||
}
|
||||
default:
|
||||
return nil, errors.Errorf("unknown slice type %s", f.Kind().String())
|
||||
}
|
||||
case reflect.Map:
|
||||
lowerCaseKeys := make(map[string][]string)
|
||||
// I dont know if this code is needed anymore, TBD
|
||||
// for k, v := range filters {
|
||||
// lowerCaseKeys[strings.ToLower(k)] = v
|
||||
// }
|
||||
s, err := json.MarshalToString(lowerCaseKeys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params.Set(fieldName, s)
|
||||
default:
|
||||
return nil, errors.Errorf("unknown type %s", f.Kind().String())
|
||||
}
|
||||
}
|
||||
return params, nil
|
||||
}
|
||||
|
||||
// WithForce
|
||||
func (o *RemoveOptions) WithForce(value bool) *RemoveOptions {
|
||||
v := &value
|
||||
o.Force = v
|
||||
return o
|
||||
}
|
@ -41,17 +41,19 @@ func BatchRemove(ctx context.Context, images []string, opts entities.ImageRemove
|
||||
return &report.ImageRemoveReport, errorhandling.StringsToErrors(report.Errors)
|
||||
}
|
||||
|
||||
// Remove removes an image from the local storage. Use force to remove an
|
||||
// Remove removes an image from the local storage. Use optional force option to remove an
|
||||
// image, even if it's used by containers.
|
||||
func Remove(ctx context.Context, nameOrID string, force bool) (*entities.ImageRemoveReport, error) {
|
||||
func Remove(ctx context.Context, nameOrID string, options *RemoveOptions) (*entities.ImageRemoveReport, error) {
|
||||
var report handlers.LibpodImagesRemoveReport
|
||||
conn, err := bindings.GetClient(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params := url.Values{}
|
||||
params.Set("force", strconv.FormatBool(force))
|
||||
params, err := options.ToParams()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err := conn.DoRequest(nil, http.MethodDelete, "/images/%s", params, nil, nameOrID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
8
pkg/bindings/images/types.go
Normal file
8
pkg/bindings/images/types.go
Normal file
@ -0,0 +1,8 @@
|
||||
package images
|
||||
|
||||
//go:generate go run ../generator/generator.go RemoveOptions
|
||||
// RemoveOptions are optional options for image removal
|
||||
type RemoveOptions struct {
|
||||
// Forces removes all containers based on the image
|
||||
Force *bool
|
||||
}
|
@ -84,7 +84,7 @@ var _ = Describe("Podman images", func() {
|
||||
// Test to validate the remove image api
|
||||
It("remove image", func() {
|
||||
// Remove invalid image should be a 404
|
||||
response, err := images.Remove(bt.conn, "foobar5000", false)
|
||||
response, err := images.Remove(bt.conn, "foobar5000", nil)
|
||||
Expect(err).ToNot(BeNil())
|
||||
Expect(response).To(BeNil())
|
||||
code, _ := bindings.CheckResponseCode(err)
|
||||
@ -93,7 +93,7 @@ var _ = Describe("Podman images", func() {
|
||||
// Remove an image by name, validate image is removed and error is nil
|
||||
inspectData, err := images.GetImage(bt.conn, busybox.shortName, nil)
|
||||
Expect(err).To(BeNil())
|
||||
response, err = images.Remove(bt.conn, busybox.shortName, false)
|
||||
response, err = images.Remove(bt.conn, busybox.shortName, nil)
|
||||
Expect(err).To(BeNil())
|
||||
code, _ = bindings.CheckResponseCode(err)
|
||||
|
||||
@ -113,12 +113,13 @@ var _ = Describe("Podman images", func() {
|
||||
|
||||
// try to remove the image "alpine". This should fail since we are not force
|
||||
// deleting hence image cannot be deleted until the container is deleted.
|
||||
response, err = images.Remove(bt.conn, alpine.shortName, false)
|
||||
response, err = images.Remove(bt.conn, alpine.shortName, nil)
|
||||
code, _ = bindings.CheckResponseCode(err)
|
||||
Expect(code).To(BeNumerically("==", http.StatusConflict))
|
||||
|
||||
// Removing the image "alpine" where force = true
|
||||
response, err = images.Remove(bt.conn, alpine.shortName, true)
|
||||
options := images.RemoveOptions{}
|
||||
response, err = images.Remove(bt.conn, alpine.shortName, options.WithForce(true))
|
||||
Expect(err).To(BeNil())
|
||||
// To be extra sure, check if the previously created container
|
||||
// is gone as well.
|
||||
@ -213,7 +214,7 @@ var _ = Describe("Podman images", func() {
|
||||
|
||||
It("Load|Import Image", func() {
|
||||
// load an image
|
||||
_, err := images.Remove(bt.conn, alpine.name, false)
|
||||
_, err := images.Remove(bt.conn, alpine.name, nil)
|
||||
Expect(err).To(BeNil())
|
||||
exists, err := images.Exists(bt.conn, alpine.name)
|
||||
Expect(err).To(BeNil())
|
||||
@ -231,7 +232,7 @@ var _ = Describe("Podman images", func() {
|
||||
// load with a repo name
|
||||
f, err = os.Open(filepath.Join(ImageCacheDir, alpine.tarballName))
|
||||
Expect(err).To(BeNil())
|
||||
_, err = images.Remove(bt.conn, alpine.name, false)
|
||||
_, err = images.Remove(bt.conn, alpine.name, nil)
|
||||
Expect(err).To(BeNil())
|
||||
exists, err = images.Exists(bt.conn, alpine.name)
|
||||
Expect(err).To(BeNil())
|
||||
@ -247,7 +248,7 @@ var _ = Describe("Podman images", func() {
|
||||
// load with a bad repo name should trigger a 500
|
||||
f, err = os.Open(filepath.Join(ImageCacheDir, alpine.tarballName))
|
||||
Expect(err).To(BeNil())
|
||||
_, err = images.Remove(bt.conn, alpine.name, false)
|
||||
_, err = images.Remove(bt.conn, alpine.name, nil)
|
||||
Expect(err).To(BeNil())
|
||||
exists, err = images.Exists(bt.conn, alpine.name)
|
||||
Expect(err).To(BeNil())
|
||||
@ -275,7 +276,7 @@ var _ = Describe("Podman images", func() {
|
||||
|
||||
It("Import Image", func() {
|
||||
// load an image
|
||||
_, err = images.Remove(bt.conn, alpine.name, false)
|
||||
_, err = images.Remove(bt.conn, alpine.name, nil)
|
||||
Expect(err).To(BeNil())
|
||||
exists, err := images.Exists(bt.conn, alpine.name)
|
||||
Expect(err).To(BeNil())
|
||||
|
@ -47,7 +47,7 @@ var _ = Describe("Podman containers ", func() {
|
||||
code, _ := bindings.CheckResponseCode(err)
|
||||
Expect(code).To(BeNumerically("==", http.StatusInternalServerError))
|
||||
|
||||
_, err = images.Remove(bt.conn, id, false)
|
||||
_, err = images.Remove(bt.conn, id, nil)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
// create manifest list with images
|
||||
|
Reference in New Issue
Block a user