terraform: modify the ResourceProvider API super hardcore
This commit is contained in:
parent
4b0f970659
commit
95f3e626a5
|
@ -514,7 +514,9 @@ func (c *Context) applyWalkFn() depgraph.WalkFunc {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
diff, err = r.Provider.Diff(r.State, r.Config)
|
is := new(InstanceState) // TODO(armon): completely broken
|
||||||
|
info := new(InstanceInfo)
|
||||||
|
diff, err = r.Provider.Diff(info, is, r.Config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -557,9 +559,14 @@ func (c *Context) applyWalkFn() depgraph.WalkFunc {
|
||||||
handleHook(h.PreApply(r.Id, r.State, diff))
|
handleHook(h.PreApply(r.Id, r.State, diff))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(armon): completely broken, added to compile
|
||||||
|
var rs *ResourceState
|
||||||
|
is := new(InstanceState)
|
||||||
|
info := new(InstanceInfo)
|
||||||
|
|
||||||
// With the completed diff, apply!
|
// With the completed diff, apply!
|
||||||
log.Printf("[DEBUG] %s: Executing Apply", r.Id)
|
log.Printf("[DEBUG] %s: Executing Apply", r.Id)
|
||||||
rs, applyerr := r.Provider.Apply(r.State, diff)
|
is, applyerr := r.Provider.Apply(info, is, diff)
|
||||||
|
|
||||||
var errs []error
|
var errs []error
|
||||||
if applyerr != nil {
|
if applyerr != nil {
|
||||||
|
@ -614,7 +621,9 @@ func (c *Context) applyWalkFn() depgraph.WalkFunc {
|
||||||
handleHook(h.PreProvisionResource(r.Id, r.State))
|
handleHook(h.PreProvisionResource(r.Id, r.State))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.applyProvisioners(r, rs); err != nil {
|
// TODO(armon): I renamed "rs" to "is" to get things to
|
||||||
|
// compile.
|
||||||
|
if err := c.applyProvisioners(r, is); err != nil {
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
tainted = true
|
tainted = true
|
||||||
}
|
}
|
||||||
|
@ -654,11 +663,11 @@ func (c *Context) applyWalkFn() depgraph.WalkFunc {
|
||||||
|
|
||||||
// applyProvisioners is used to run any provisioners a resource has
|
// applyProvisioners is used to run any provisioners a resource has
|
||||||
// defined after the resource creation has already completed.
|
// defined after the resource creation has already completed.
|
||||||
func (c *Context) applyProvisioners(r *Resource, rs *ResourceState) error {
|
func (c *Context) applyProvisioners(r *Resource, is *InstanceState) error {
|
||||||
// Store the original connection info, restore later
|
// Store the original connection info, restore later
|
||||||
origConnInfo := rs.Primary.Ephemeral.ConnInfo
|
origConnInfo := is.Ephemeral.ConnInfo
|
||||||
defer func() {
|
defer func() {
|
||||||
rs.Primary.Ephemeral.ConnInfo = origConnInfo
|
is.Ephemeral.ConnInfo = origConnInfo
|
||||||
}()
|
}()
|
||||||
|
|
||||||
for _, prov := range r.Provisioners {
|
for _, prov := range r.Provisioners {
|
||||||
|
@ -701,14 +710,14 @@ func (c *Context) applyProvisioners(r *Resource, rs *ResourceState) error {
|
||||||
overlay[k] = fmt.Sprintf("%v", vt)
|
overlay[k] = fmt.Sprintf("%v", vt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rs.Primary.Ephemeral.ConnInfo = overlay
|
is.Ephemeral.ConnInfo = overlay
|
||||||
|
|
||||||
// Invoke the Provisioner
|
// Invoke the Provisioner
|
||||||
for _, h := range c.hooks {
|
for _, h := range c.hooks {
|
||||||
handleHook(h.PreProvision(r.Id, prov.Type))
|
handleHook(h.PreProvision(r.Id, prov.Type))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := prov.Provisioner.Apply(rs, prov.Config); err != nil {
|
if err := prov.Provisioner.Apply(is, prov.Config); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -753,7 +762,9 @@ func (c *Context) planWalkFn(result *Plan) depgraph.WalkFunc {
|
||||||
state = new(ResourceState)
|
state = new(ResourceState)
|
||||||
state.Type = r.State.Type
|
state.Type = r.State.Type
|
||||||
}
|
}
|
||||||
diff, err = r.Provider.Diff(state, r.Config)
|
is := new(InstanceState) // TODO(armon): completely broken
|
||||||
|
info := new(InstanceInfo) // TODO(armon): completely broken
|
||||||
|
diff, err = r.Provider.Diff(info, is, r.Config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -797,7 +808,9 @@ func (c *Context) planWalkFn(result *Plan) depgraph.WalkFunc {
|
||||||
|
|
||||||
// Determine the new state and update variables
|
// Determine the new state and update variables
|
||||||
if !diff.Empty() {
|
if !diff.Empty() {
|
||||||
r.State = r.State.MergeDiff(diff)
|
// TODO(armon): commented to compile, this is important
|
||||||
|
// but needs to be fixed to work with InstanceState
|
||||||
|
//r.State = r.State.MergeDiff(diff)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update our internal state so that variable computation works
|
// Update our internal state so that variable computation works
|
||||||
|
@ -852,7 +865,12 @@ func (c *Context) refreshWalkFn() depgraph.WalkFunc {
|
||||||
handleHook(h.PreRefresh(r.Id, r.State))
|
handleHook(h.PreRefresh(r.Id, r.State))
|
||||||
}
|
}
|
||||||
|
|
||||||
rs, err := r.Provider.Refresh(r.State)
|
// TODO(armon): completely broken
|
||||||
|
var rs *ResourceState
|
||||||
|
is := new(InstanceState)
|
||||||
|
info := new(InstanceInfo)
|
||||||
|
|
||||||
|
is, err := r.Provider.Refresh(info, is)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -411,7 +411,7 @@ func TestContextApply_badDiff(t *testing.T) {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
p.DiffFn = func(*ResourceState, *ResourceConfig) (*ResourceDiff, error) {
|
p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*ResourceDiff, error) {
|
||||||
return &ResourceDiff{
|
return &ResourceDiff{
|
||||||
Attributes: map[string]*ResourceAttrDiff{
|
Attributes: map[string]*ResourceAttrDiff{
|
||||||
"newp": nil,
|
"newp": nil,
|
||||||
|
@ -436,7 +436,7 @@ func TestContextApply_cancel(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
p.ApplyFn = func(*ResourceState, *ResourceDiff) (*ResourceState, error) {
|
p.ApplyFn = func(*InstanceInfo, *InstanceState, *ResourceDiff) (*InstanceState, error) {
|
||||||
if !stopped {
|
if !stopped {
|
||||||
stopped = true
|
stopped = true
|
||||||
go ctx.Stop()
|
go ctx.Stop()
|
||||||
|
@ -448,16 +448,14 @@ func TestContextApply_cancel(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ResourceState{
|
return &InstanceState{
|
||||||
Primary: &InstanceState{
|
ID: "foo",
|
||||||
ID: "foo",
|
Attributes: map[string]string{
|
||||||
Attributes: map[string]string{
|
"num": "2",
|
||||||
"num": "2",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
p.DiffFn = func(*ResourceState, *ResourceConfig) (*ResourceDiff, error) {
|
p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*ResourceDiff, error) {
|
||||||
return &ResourceDiff{
|
return &ResourceDiff{
|
||||||
Attributes: map[string]*ResourceAttrDiff{
|
Attributes: map[string]*ResourceAttrDiff{
|
||||||
"num": &ResourceAttrDiff{
|
"num": &ResourceAttrDiff{
|
||||||
|
@ -542,7 +540,7 @@ func TestContextApply_nilDiff(t *testing.T) {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
p.DiffFn = func(*ResourceState, *ResourceConfig) (*ResourceDiff, error) {
|
p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*ResourceDiff, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -557,7 +555,7 @@ func TestContextApply_Provisioner_compute(t *testing.T) {
|
||||||
pr := testProvisioner()
|
pr := testProvisioner()
|
||||||
p.ApplyFn = testApplyFn
|
p.ApplyFn = testApplyFn
|
||||||
p.DiffFn = testDiffFn
|
p.DiffFn = testDiffFn
|
||||||
pr.ApplyFn = func(rs *ResourceState, c *ResourceConfig) error {
|
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
||||||
val, ok := c.Config["foo"]
|
val, ok := c.Config["foo"]
|
||||||
if !ok || val != "computed_dynamical" {
|
if !ok || val != "computed_dynamical" {
|
||||||
t.Fatalf("bad value for foo: %v %#v", val, c)
|
t.Fatalf("bad value for foo: %v %#v", val, c)
|
||||||
|
@ -606,7 +604,7 @@ func TestContextApply_provisionerFail(t *testing.T) {
|
||||||
p.ApplyFn = testApplyFn
|
p.ApplyFn = testApplyFn
|
||||||
p.DiffFn = testDiffFn
|
p.DiffFn = testDiffFn
|
||||||
|
|
||||||
pr.ApplyFn = func(*ResourceState, *ResourceConfig) error {
|
pr.ApplyFn = func(*InstanceState, *ResourceConfig) error {
|
||||||
return fmt.Errorf("EXPLOSION")
|
return fmt.Errorf("EXPLOSION")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -645,7 +643,7 @@ func TestContextApply_provisionerResourceRef(t *testing.T) {
|
||||||
pr := testProvisioner()
|
pr := testProvisioner()
|
||||||
p.ApplyFn = testApplyFn
|
p.ApplyFn = testApplyFn
|
||||||
p.DiffFn = testDiffFn
|
p.DiffFn = testDiffFn
|
||||||
pr.ApplyFn = func(rs *ResourceState, c *ResourceConfig) error {
|
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
||||||
val, ok := c.Config["foo"]
|
val, ok := c.Config["foo"]
|
||||||
if !ok || val != "2" {
|
if !ok || val != "2" {
|
||||||
t.Fatalf("bad value for foo: %v %#v", val, c)
|
t.Fatalf("bad value for foo: %v %#v", val, c)
|
||||||
|
@ -711,7 +709,7 @@ func TestContextApply_outputDiffVars(t *testing.T) {
|
||||||
State: s,
|
State: s,
|
||||||
})
|
})
|
||||||
|
|
||||||
p.ApplyFn = func(s *ResourceState, d *ResourceDiff) (*ResourceState, error) {
|
p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *ResourceDiff) (*InstanceState, error) {
|
||||||
for k, ad := range d.Attributes {
|
for k, ad := range d.Attributes {
|
||||||
if ad.NewComputed {
|
if ad.NewComputed {
|
||||||
return nil, fmt.Errorf("%s: computed", k)
|
return nil, fmt.Errorf("%s: computed", k)
|
||||||
|
@ -719,10 +717,10 @@ func TestContextApply_outputDiffVars(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
result := s.MergeDiff(d)
|
result := s.MergeDiff(d)
|
||||||
result.Primary.ID = "foo"
|
result.ID = "foo"
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
p.DiffFn = func(*ResourceState, *ResourceConfig) (*ResourceDiff, error) {
|
p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*ResourceDiff, error) {
|
||||||
return &ResourceDiff{
|
return &ResourceDiff{
|
||||||
Attributes: map[string]*ResourceAttrDiff{
|
Attributes: map[string]*ResourceAttrDiff{
|
||||||
"foo": &ResourceAttrDiff{
|
"foo": &ResourceAttrDiff{
|
||||||
|
@ -749,13 +747,13 @@ func TestContextApply_Provisioner_ConnInfo(t *testing.T) {
|
||||||
p := testProvider("aws")
|
p := testProvider("aws")
|
||||||
pr := testProvisioner()
|
pr := testProvisioner()
|
||||||
|
|
||||||
p.ApplyFn = func(s *ResourceState, d *ResourceDiff) (*ResourceState, error) {
|
p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *ResourceDiff) (*InstanceState, error) {
|
||||||
if s.Primary.Ephemeral.ConnInfo == nil {
|
if s.Ephemeral.ConnInfo == nil {
|
||||||
t.Fatalf("ConnInfo not initialized")
|
t.Fatalf("ConnInfo not initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
result, _ := testApplyFn(s, d)
|
result, _ := testApplyFn(info, s, d)
|
||||||
result.Primary.Ephemeral.ConnInfo = map[string]string{
|
result.Ephemeral.ConnInfo = map[string]string{
|
||||||
"type": "ssh",
|
"type": "ssh",
|
||||||
"host": "127.0.0.1",
|
"host": "127.0.0.1",
|
||||||
"port": "22",
|
"port": "22",
|
||||||
|
@ -764,8 +762,8 @@ func TestContextApply_Provisioner_ConnInfo(t *testing.T) {
|
||||||
}
|
}
|
||||||
p.DiffFn = testDiffFn
|
p.DiffFn = testDiffFn
|
||||||
|
|
||||||
pr.ApplyFn = func(rs *ResourceState, c *ResourceConfig) error {
|
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
||||||
conn := rs.Primary.Ephemeral.ConnInfo
|
conn := rs.Ephemeral.ConnInfo
|
||||||
if conn["type"] != "telnet" {
|
if conn["type"] != "telnet" {
|
||||||
t.Fatalf("Bad: %#v", conn)
|
t.Fatalf("Bad: %#v", conn)
|
||||||
}
|
}
|
||||||
|
@ -937,16 +935,16 @@ func TestContextApply_destroyOrphan(t *testing.T) {
|
||||||
State: s,
|
State: s,
|
||||||
})
|
})
|
||||||
|
|
||||||
p.ApplyFn = func(s *ResourceState, d *ResourceDiff) (*ResourceState, error) {
|
p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *ResourceDiff) (*InstanceState, error) {
|
||||||
if d.Destroy {
|
if d.Destroy {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
result := s.MergeDiff(d)
|
result := s.MergeDiff(d)
|
||||||
result.Primary.ID = "foo"
|
result.ID = "foo"
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
p.DiffFn = func(*ResourceState, *ResourceConfig) (*ResourceDiff, error) {
|
p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*ResourceDiff, error) {
|
||||||
return &ResourceDiff{
|
return &ResourceDiff{
|
||||||
Attributes: map[string]*ResourceAttrDiff{
|
Attributes: map[string]*ResourceAttrDiff{
|
||||||
"num": &ResourceAttrDiff{
|
"num": &ResourceAttrDiff{
|
||||||
|
@ -983,27 +981,23 @@ func TestContextApply_error(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
p.ApplyFn = func(*ResourceState, *ResourceDiff) (*ResourceState, error) {
|
p.ApplyFn = func(*InstanceInfo, *InstanceState, *ResourceDiff) (*InstanceState, error) {
|
||||||
if errored {
|
if errored {
|
||||||
state := &ResourceState{
|
state := &InstanceState{
|
||||||
Primary: &InstanceState{
|
ID: "bar",
|
||||||
ID: "bar",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
return state, fmt.Errorf("error")
|
return state, fmt.Errorf("error")
|
||||||
}
|
}
|
||||||
errored = true
|
errored = true
|
||||||
|
|
||||||
return &ResourceState{
|
return &InstanceState{
|
||||||
Primary: &InstanceState{
|
ID: "foo",
|
||||||
ID: "foo",
|
Attributes: map[string]string{
|
||||||
Attributes: map[string]string{
|
"num": "2",
|
||||||
"num": "2",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
p.DiffFn = func(*ResourceState, *ResourceConfig) (*ResourceDiff, error) {
|
p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*ResourceDiff, error) {
|
||||||
return &ResourceDiff{
|
return &ResourceDiff{
|
||||||
Attributes: map[string]*ResourceAttrDiff{
|
Attributes: map[string]*ResourceAttrDiff{
|
||||||
"num": &ResourceAttrDiff{
|
"num": &ResourceAttrDiff{
|
||||||
|
@ -1057,22 +1051,20 @@ func TestContextApply_errorPartial(t *testing.T) {
|
||||||
State: s,
|
State: s,
|
||||||
})
|
})
|
||||||
|
|
||||||
p.ApplyFn = func(s *ResourceState, d *ResourceDiff) (*ResourceState, error) {
|
p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *ResourceDiff) (*InstanceState, error) {
|
||||||
if errored {
|
if errored {
|
||||||
return s, fmt.Errorf("error")
|
return s, fmt.Errorf("error")
|
||||||
}
|
}
|
||||||
errored = true
|
errored = true
|
||||||
|
|
||||||
return &ResourceState{
|
return &InstanceState{
|
||||||
Primary: &InstanceState{
|
ID: "foo",
|
||||||
ID: "foo",
|
Attributes: map[string]string{
|
||||||
Attributes: map[string]string{
|
"num": "2",
|
||||||
"num": "2",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
p.DiffFn = func(*ResourceState, *ResourceConfig) (*ResourceDiff, error) {
|
p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*ResourceDiff, error) {
|
||||||
return &ResourceDiff{
|
return &ResourceDiff{
|
||||||
Attributes: map[string]*ResourceAttrDiff{
|
Attributes: map[string]*ResourceAttrDiff{
|
||||||
"num": &ResourceAttrDiff{
|
"num": &ResourceAttrDiff{
|
||||||
|
@ -1143,16 +1135,16 @@ func TestContextApply_idAttr(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
p.ApplyFn = func(s *ResourceState, d *ResourceDiff) (*ResourceState, error) {
|
p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *ResourceDiff) (*InstanceState, error) {
|
||||||
result := s.MergeDiff(d)
|
result := s.MergeDiff(d)
|
||||||
result.Primary.ID = "foo"
|
result.ID = "foo"
|
||||||
result.Primary.Attributes = map[string]string{
|
result.Attributes = map[string]string{
|
||||||
"id": "bar",
|
"id": "bar",
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
p.DiffFn = func(*ResourceState, *ResourceConfig) (*ResourceDiff, error) {
|
p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*ResourceDiff, error) {
|
||||||
return &ResourceDiff{
|
return &ResourceDiff{
|
||||||
Attributes: map[string]*ResourceAttrDiff{
|
Attributes: map[string]*ResourceAttrDiff{
|
||||||
"num": &ResourceAttrDiff{
|
"num": &ResourceAttrDiff{
|
||||||
|
@ -1762,9 +1754,11 @@ func TestContextPlan_diffVar(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
p.DiffFn = func(
|
p.DiffFn = func(
|
||||||
s *ResourceState, c *ResourceConfig) (*ResourceDiff, error) {
|
info *InstanceInfo,
|
||||||
if s.Primary.ID != "bar" {
|
s *InstanceState,
|
||||||
return testDiffFn(s, c)
|
c *ResourceConfig) (*ResourceDiff, error) {
|
||||||
|
if s.ID != "bar" {
|
||||||
|
return testDiffFn(info, s, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ResourceDiff{
|
return &ResourceDiff{
|
||||||
|
@ -1993,10 +1987,8 @@ func TestContextRefresh(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
p.RefreshFn = nil
|
p.RefreshFn = nil
|
||||||
p.RefreshReturn = &ResourceState{
|
p.RefreshReturn = &InstanceState{
|
||||||
Primary: &InstanceState{
|
ID: "foo",
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s, err := ctx.Refresh()
|
s, err := ctx.Refresh()
|
||||||
|
@ -2007,7 +1999,7 @@ func TestContextRefresh(t *testing.T) {
|
||||||
if !p.RefreshCalled {
|
if !p.RefreshCalled {
|
||||||
t.Fatal("refresh should be called")
|
t.Fatal("refresh should be called")
|
||||||
}
|
}
|
||||||
if p.RefreshState.Primary.ID != "foo" {
|
if p.RefreshState.ID != "foo" {
|
||||||
t.Fatalf("bad: %#v", p.RefreshState)
|
t.Fatalf("bad: %#v", p.RefreshState)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(mod.Resources["aws_instance.web"], p.RefreshReturn) {
|
if !reflect.DeepEqual(mod.Resources["aws_instance.web"], p.RefreshReturn) {
|
||||||
|
@ -2072,10 +2064,8 @@ func TestContextRefresh_ignoreUncreated(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
p.RefreshFn = nil
|
p.RefreshFn = nil
|
||||||
p.RefreshReturn = &ResourceState{
|
p.RefreshReturn = &InstanceState{
|
||||||
Primary: &InstanceState{
|
ID: "foo",
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := ctx.Refresh()
|
_, err := ctx.Refresh()
|
||||||
|
@ -2157,10 +2147,8 @@ func TestContextRefresh_state(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
p.RefreshFn = nil
|
p.RefreshFn = nil
|
||||||
p.RefreshReturn = &ResourceState{
|
p.RefreshReturn = &InstanceState{
|
||||||
Primary: &InstanceState{
|
ID: "foo",
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s, err := ctx.Refresh()
|
s, err := ctx.Refresh()
|
||||||
|
@ -2206,10 +2194,8 @@ func TestContextRefresh_vars(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
p.RefreshFn = nil
|
p.RefreshFn = nil
|
||||||
p.RefreshReturn = &ResourceState{
|
p.RefreshReturn = &InstanceState{
|
||||||
Primary: &InstanceState{
|
ID: "foo",
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s, err := ctx.Refresh()
|
s, err := ctx.Refresh()
|
||||||
|
@ -2220,7 +2206,7 @@ func TestContextRefresh_vars(t *testing.T) {
|
||||||
if !p.RefreshCalled {
|
if !p.RefreshCalled {
|
||||||
t.Fatal("refresh should be called")
|
t.Fatal("refresh should be called")
|
||||||
}
|
}
|
||||||
if p.RefreshState.Primary.ID != "foo" {
|
if p.RefreshState.ID != "foo" {
|
||||||
t.Fatalf("bad: %#v", p.RefreshState)
|
t.Fatalf("bad: %#v", p.RefreshState)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(mod.Resources["aws_instance.web"], p.RefreshReturn) {
|
if !reflect.DeepEqual(mod.Resources["aws_instance.web"], p.RefreshReturn) {
|
||||||
|
@ -2239,8 +2225,9 @@ func testContext(t *testing.T, opts *ContextOpts) *Context {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testApplyFn(
|
func testApplyFn(
|
||||||
s *ResourceState,
|
info *InstanceInfo,
|
||||||
d *ResourceDiff) (*ResourceState, error) {
|
s *InstanceState,
|
||||||
|
d *ResourceDiff) (*InstanceState, error) {
|
||||||
if d.Destroy {
|
if d.Destroy {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -2250,25 +2237,28 @@ func testApplyFn(
|
||||||
id = idAttr.New
|
id = idAttr.New
|
||||||
}
|
}
|
||||||
|
|
||||||
result := &ResourceState{
|
result := &InstanceState{
|
||||||
Primary: &InstanceState{
|
ID: id,
|
||||||
ID: id,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if d != nil {
|
if d != nil {
|
||||||
result = result.MergeDiff(d)
|
result = result.MergeDiff(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
if depAttr, ok := d.Attributes["dep"]; ok {
|
// TODO(armon): commenting this out to compile, but you're in the
|
||||||
result.Dependencies = []string{depAttr.New}
|
// process of removing this, too anyways. Remove when safe.
|
||||||
}
|
/*
|
||||||
|
if depAttr, ok := d.Attributes["dep"]; ok {
|
||||||
|
result.Dependencies = []string{depAttr.New}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDiffFn(
|
func testDiffFn(
|
||||||
s *ResourceState,
|
info *InstanceInfo,
|
||||||
|
s *InstanceState,
|
||||||
c *ResourceConfig) (*ResourceDiff, error) {
|
c *ResourceConfig) (*ResourceDiff, error) {
|
||||||
var diff ResourceDiff
|
var diff ResourceDiff
|
||||||
diff.Attributes = make(map[string]*ResourceAttrDiff)
|
diff.Attributes = make(map[string]*ResourceAttrDiff)
|
||||||
|
@ -2338,7 +2328,7 @@ func testDiffFn(
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
old, ok := s.Primary.Attributes[k]
|
old, ok := s.Attributes[k]
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -2350,7 +2340,7 @@ func testDiffFn(
|
||||||
if !diff.Empty() {
|
if !diff.Empty() {
|
||||||
diff.Attributes["type"] = &ResourceAttrDiff{
|
diff.Attributes["type"] = &ResourceAttrDiff{
|
||||||
Old: "",
|
Old: "",
|
||||||
New: s.Type,
|
New: info.Type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2359,7 +2349,7 @@ func testDiffFn(
|
||||||
|
|
||||||
func testProvider(prefix string) *MockResourceProvider {
|
func testProvider(prefix string) *MockResourceProvider {
|
||||||
p := new(MockResourceProvider)
|
p := new(MockResourceProvider)
|
||||||
p.RefreshFn = func(s *ResourceState) (*ResourceState, error) {
|
p.RefreshFn = func(info *InstanceInfo, s *InstanceState) (*InstanceState, error) {
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
p.ResourcesReturn = []ResourceType{
|
p.ResourcesReturn = []ResourceType{
|
||||||
|
|
|
@ -46,18 +46,27 @@ type ResourceProvider interface {
|
||||||
// If the resource state given has an empty ID, then a new resource
|
// If the resource state given has an empty ID, then a new resource
|
||||||
// is expected to be created.
|
// is expected to be created.
|
||||||
Apply(
|
Apply(
|
||||||
*ResourceState,
|
*InstanceInfo,
|
||||||
*ResourceDiff) (*ResourceState, error)
|
*InstanceState,
|
||||||
|
*ResourceDiff) (*InstanceState, error)
|
||||||
|
|
||||||
// Diff diffs a resource versus a desired state and returns
|
// Diff diffs a resource versus a desired state and returns
|
||||||
// a diff.
|
// a diff.
|
||||||
Diff(
|
Diff(
|
||||||
*ResourceState,
|
*InstanceInfo,
|
||||||
|
*InstanceState,
|
||||||
*ResourceConfig) (*ResourceDiff, error)
|
*ResourceConfig) (*ResourceDiff, error)
|
||||||
|
|
||||||
// Refresh refreshes a resource and updates all of its attributes
|
// Refresh refreshes a resource and updates all of its attributes
|
||||||
// with the latest information.
|
// with the latest information.
|
||||||
Refresh(*ResourceState) (*ResourceState, error)
|
Refresh(*InstanceInfo, *InstanceState) (*InstanceState, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceInfo is used to hold information about the instance and/or
|
||||||
|
// resource being modified.
|
||||||
|
type InstanceInfo struct {
|
||||||
|
// Type is the resource type of this instance
|
||||||
|
Type string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResourceType is a type of resource that a resource provider can manage.
|
// ResourceType is a type of resource that a resource provider can manage.
|
||||||
|
|
|
@ -13,24 +13,27 @@ type MockResourceProvider struct {
|
||||||
Meta interface{}
|
Meta interface{}
|
||||||
|
|
||||||
ApplyCalled bool
|
ApplyCalled bool
|
||||||
ApplyState *ResourceState
|
ApplyInfo *InstanceInfo
|
||||||
|
ApplyState *InstanceState
|
||||||
ApplyDiff *ResourceDiff
|
ApplyDiff *ResourceDiff
|
||||||
ApplyFn func(*ResourceState, *ResourceDiff) (*ResourceState, error)
|
ApplyFn func(*InstanceInfo, *InstanceState, *ResourceDiff) (*InstanceState, error)
|
||||||
ApplyReturn *ResourceState
|
ApplyReturn *InstanceState
|
||||||
ApplyReturnError error
|
ApplyReturnError error
|
||||||
ConfigureCalled bool
|
ConfigureCalled bool
|
||||||
ConfigureConfig *ResourceConfig
|
ConfigureConfig *ResourceConfig
|
||||||
ConfigureReturnError error
|
ConfigureReturnError error
|
||||||
DiffCalled bool
|
DiffCalled bool
|
||||||
DiffState *ResourceState
|
DiffInfo *InstanceInfo
|
||||||
|
DiffState *InstanceState
|
||||||
DiffDesired *ResourceConfig
|
DiffDesired *ResourceConfig
|
||||||
DiffFn func(*ResourceState, *ResourceConfig) (*ResourceDiff, error)
|
DiffFn func(*InstanceInfo, *InstanceState, *ResourceConfig) (*ResourceDiff, error)
|
||||||
DiffReturn *ResourceDiff
|
DiffReturn *ResourceDiff
|
||||||
DiffReturnError error
|
DiffReturnError error
|
||||||
RefreshCalled bool
|
RefreshCalled bool
|
||||||
RefreshState *ResourceState
|
RefreshInfo *InstanceInfo
|
||||||
RefreshFn func(*ResourceState) (*ResourceState, error)
|
RefreshState *InstanceState
|
||||||
RefreshReturn *ResourceState
|
RefreshFn func(*InstanceInfo, *InstanceState) (*InstanceState, error)
|
||||||
|
RefreshReturn *InstanceState
|
||||||
RefreshReturnError error
|
RefreshReturnError error
|
||||||
ResourcesCalled bool
|
ResourcesCalled bool
|
||||||
ResourcesReturn []ResourceType
|
ResourcesReturn []ResourceType
|
||||||
|
@ -80,47 +83,53 @@ func (p *MockResourceProvider) Configure(c *ResourceConfig) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *MockResourceProvider) Apply(
|
func (p *MockResourceProvider) Apply(
|
||||||
state *ResourceState,
|
info *InstanceInfo,
|
||||||
diff *ResourceDiff) (*ResourceState, error) {
|
state *InstanceState,
|
||||||
|
diff *ResourceDiff) (*InstanceState, error) {
|
||||||
p.Lock()
|
p.Lock()
|
||||||
defer p.Unlock()
|
defer p.Unlock()
|
||||||
|
|
||||||
p.ApplyCalled = true
|
p.ApplyCalled = true
|
||||||
|
p.ApplyInfo = info
|
||||||
p.ApplyState = state
|
p.ApplyState = state
|
||||||
p.ApplyDiff = diff
|
p.ApplyDiff = diff
|
||||||
if p.ApplyFn != nil {
|
if p.ApplyFn != nil {
|
||||||
return p.ApplyFn(state, diff)
|
return p.ApplyFn(info, state, diff)
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.ApplyReturn, p.ApplyReturnError
|
return p.ApplyReturn, p.ApplyReturnError
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *MockResourceProvider) Diff(
|
func (p *MockResourceProvider) Diff(
|
||||||
state *ResourceState,
|
info *InstanceInfo,
|
||||||
|
state *InstanceState,
|
||||||
desired *ResourceConfig) (*ResourceDiff, error) {
|
desired *ResourceConfig) (*ResourceDiff, error) {
|
||||||
p.Lock()
|
p.Lock()
|
||||||
defer p.Unlock()
|
defer p.Unlock()
|
||||||
|
|
||||||
p.DiffCalled = true
|
p.DiffCalled = true
|
||||||
|
p.DiffInfo = info
|
||||||
p.DiffState = state
|
p.DiffState = state
|
||||||
p.DiffDesired = desired
|
p.DiffDesired = desired
|
||||||
if p.DiffFn != nil {
|
if p.DiffFn != nil {
|
||||||
return p.DiffFn(state, desired)
|
return p.DiffFn(info, state, desired)
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.DiffReturn, p.DiffReturnError
|
return p.DiffReturn, p.DiffReturnError
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *MockResourceProvider) Refresh(
|
func (p *MockResourceProvider) Refresh(
|
||||||
s *ResourceState) (*ResourceState, error) {
|
info *InstanceInfo,
|
||||||
|
s *InstanceState) (*InstanceState, error) {
|
||||||
p.Lock()
|
p.Lock()
|
||||||
defer p.Unlock()
|
defer p.Unlock()
|
||||||
|
|
||||||
p.RefreshCalled = true
|
p.RefreshCalled = true
|
||||||
|
p.RefreshInfo = info
|
||||||
p.RefreshState = s
|
p.RefreshState = s
|
||||||
|
|
||||||
if p.RefreshFn != nil {
|
if p.RefreshFn != nil {
|
||||||
return p.RefreshFn(s)
|
return p.RefreshFn(info, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.RefreshReturn, p.RefreshReturnError
|
return p.RefreshReturn, p.RefreshReturnError
|
||||||
|
|
|
@ -20,7 +20,7 @@ type ResourceProvisioner interface {
|
||||||
// resource state along with an error. Instead of a diff, the ResourceConfig
|
// resource state along with an error. Instead of a diff, the ResourceConfig
|
||||||
// is provided since provisioners only run after a resource has been
|
// is provided since provisioners only run after a resource has been
|
||||||
// newly created.
|
// newly created.
|
||||||
Apply(*ResourceState, *ResourceConfig) error
|
Apply(*InstanceState, *ResourceConfig) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResourceProvisionerFactory is a function type that creates a new instance
|
// ResourceProvisionerFactory is a function type that creates a new instance
|
||||||
|
|
|
@ -7,9 +7,9 @@ type MockResourceProvisioner struct {
|
||||||
Meta interface{}
|
Meta interface{}
|
||||||
|
|
||||||
ApplyCalled bool
|
ApplyCalled bool
|
||||||
ApplyState *ResourceState
|
ApplyState *InstanceState
|
||||||
ApplyConfig *ResourceConfig
|
ApplyConfig *ResourceConfig
|
||||||
ApplyFn func(*ResourceState, *ResourceConfig) error
|
ApplyFn func(*InstanceState, *ResourceConfig) error
|
||||||
ApplyReturnError error
|
ApplyReturnError error
|
||||||
|
|
||||||
ValidateCalled bool
|
ValidateCalled bool
|
||||||
|
@ -28,7 +28,7 @@ func (p *MockResourceProvisioner) Validate(c *ResourceConfig) ([]string, []error
|
||||||
return p.ValidateReturnWarns, p.ValidateReturnErrors
|
return p.ValidateReturnWarns, p.ValidateReturnErrors
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *MockResourceProvisioner) Apply(state *ResourceState, c *ResourceConfig) error {
|
func (p *MockResourceProvisioner) Apply(state *InstanceState, c *ResourceConfig) error {
|
||||||
p.ApplyCalled = true
|
p.ApplyCalled = true
|
||||||
p.ApplyState = state
|
p.ApplyState = state
|
||||||
p.ApplyConfig = c
|
p.ApplyConfig = c
|
||||||
|
|
|
@ -341,44 +341,6 @@ func (s *ResourceState) GoString() string {
|
||||||
return fmt.Sprintf("*%#v", *s)
|
return fmt.Sprintf("*%#v", *s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MergeDiff takes a ResourceDiff and merges the attributes into
|
|
||||||
// this resource state in order to generate a new state. This new
|
|
||||||
// state can be used to provide updated attribute lookups for
|
|
||||||
// variable interpolation.
|
|
||||||
//
|
|
||||||
// If the diff attribute requires computing the value, and hence
|
|
||||||
// won't be available until apply, the value is replaced with the
|
|
||||||
// computeID.
|
|
||||||
func (s *ResourceState) MergeDiff(d *ResourceDiff) *ResourceState {
|
|
||||||
var result ResourceState
|
|
||||||
if s != nil {
|
|
||||||
result = *s
|
|
||||||
}
|
|
||||||
result.init()
|
|
||||||
|
|
||||||
if s != nil {
|
|
||||||
for k, v := range s.Primary.Attributes {
|
|
||||||
result.Primary.Attributes[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if d != nil {
|
|
||||||
for k, diff := range d.Attributes {
|
|
||||||
if diff.NewRemoved {
|
|
||||||
delete(result.Primary.Attributes, k)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if diff.NewComputed {
|
|
||||||
result.Primary.Attributes[k] = config.UnknownVariableValue
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
result.Primary.Attributes[k] = diff.New
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &result
|
|
||||||
}
|
|
||||||
|
|
||||||
// InstanceState is used to track the unique state information belonging
|
// InstanceState is used to track the unique state information belonging
|
||||||
// to a given instance.
|
// to a given instance.
|
||||||
type InstanceState struct {
|
type InstanceState struct {
|
||||||
|
@ -425,6 +387,44 @@ func (i *InstanceState) deepcopy() *InstanceState {
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MergeDiff takes a ResourceDiff and merges the attributes into
|
||||||
|
// this resource state in order to generate a new state. This new
|
||||||
|
// state can be used to provide updated attribute lookups for
|
||||||
|
// variable interpolation.
|
||||||
|
//
|
||||||
|
// If the diff attribute requires computing the value, and hence
|
||||||
|
// won't be available until apply, the value is replaced with the
|
||||||
|
// computeID.
|
||||||
|
func (s *InstanceState) MergeDiff(d *ResourceDiff) *InstanceState {
|
||||||
|
var result InstanceState
|
||||||
|
if s != nil {
|
||||||
|
result = *s
|
||||||
|
}
|
||||||
|
result.init()
|
||||||
|
|
||||||
|
if s != nil {
|
||||||
|
for k, v := range s.Attributes {
|
||||||
|
result.Attributes[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if d != nil {
|
||||||
|
for k, diff := range d.Attributes {
|
||||||
|
if diff.NewRemoved {
|
||||||
|
delete(result.Attributes, k)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if diff.NewComputed {
|
||||||
|
result.Attributes[k] = config.UnknownVariableValue
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Attributes[k] = diff.New
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result
|
||||||
|
}
|
||||||
|
|
||||||
func (i *InstanceState) GoString() string {
|
func (i *InstanceState) GoString() string {
|
||||||
return fmt.Sprintf("*%#v", *i)
|
return fmt.Sprintf("*%#v", *i)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,14 +12,12 @@ import (
|
||||||
"github.com/hashicorp/terraform/config"
|
"github.com/hashicorp/terraform/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestResourceState_MergeDiff(t *testing.T) {
|
func TestInstanceState_MergeDiff(t *testing.T) {
|
||||||
rs := ResourceState{
|
is := InstanceState{
|
||||||
Primary: &InstanceState{
|
ID: "foo",
|
||||||
ID: "foo",
|
Attributes: map[string]string{
|
||||||
Attributes: map[string]string{
|
"foo": "bar",
|
||||||
"foo": "bar",
|
"port": "8000",
|
||||||
"port": "8000",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +42,7 @@ func TestResourceState_MergeDiff(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
rs2 := rs.MergeDiff(diff)
|
is2 := is.MergeDiff(diff)
|
||||||
|
|
||||||
expected := map[string]string{
|
expected := map[string]string{
|
||||||
"foo": "baz",
|
"foo": "baz",
|
||||||
|
@ -52,13 +50,13 @@ func TestResourceState_MergeDiff(t *testing.T) {
|
||||||
"baz": config.UnknownVariableValue,
|
"baz": config.UnknownVariableValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(expected, rs2.Primary.Attributes) {
|
if !reflect.DeepEqual(expected, is2.Attributes) {
|
||||||
t.Fatalf("bad: %#v", rs2.Primary.Attributes)
|
t.Fatalf("bad: %#v", is2.Attributes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResourceState_MergeDiff_nil(t *testing.T) {
|
func TestInstanceState_MergeDiff_nil(t *testing.T) {
|
||||||
var rs *ResourceState = nil
|
var is *InstanceState = nil
|
||||||
|
|
||||||
diff := &ResourceDiff{
|
diff := &ResourceDiff{
|
||||||
Attributes: map[string]*ResourceAttrDiff{
|
Attributes: map[string]*ResourceAttrDiff{
|
||||||
|
@ -69,35 +67,33 @@ func TestResourceState_MergeDiff_nil(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
rs2 := rs.MergeDiff(diff)
|
is2 := is.MergeDiff(diff)
|
||||||
|
|
||||||
expected := map[string]string{
|
expected := map[string]string{
|
||||||
"foo": "baz",
|
"foo": "baz",
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(expected, rs2.Primary.Attributes) {
|
if !reflect.DeepEqual(expected, is2.Attributes) {
|
||||||
t.Fatalf("bad: %#v", rs2.Primary.Attributes)
|
t.Fatalf("bad: %#v", is2.Attributes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResourceState_MergeDiff_nilDiff(t *testing.T) {
|
func TestInstanceState_MergeDiff_nilDiff(t *testing.T) {
|
||||||
rs := ResourceState{
|
is := InstanceState{
|
||||||
Primary: &InstanceState{
|
ID: "foo",
|
||||||
ID: "foo",
|
Attributes: map[string]string{
|
||||||
Attributes: map[string]string{
|
"foo": "bar",
|
||||||
"foo": "bar",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
rs2 := rs.MergeDiff(nil)
|
is2 := is.MergeDiff(nil)
|
||||||
|
|
||||||
expected := map[string]string{
|
expected := map[string]string{
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(expected, rs2.Primary.Attributes) {
|
if !reflect.DeepEqual(expected, is2.Attributes) {
|
||||||
t.Fatalf("bad: %#v", rs2.Primary.Attributes)
|
t.Fatalf("bad: %#v", is2.Attributes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue