From 1880aa7fa5e3ddf5838e5874d3710d2c5daf78a4 Mon Sep 17 00:00:00 2001 From: zengchen1024 Date: Wed, 6 Dec 2017 11:42:14 +0800 Subject: [PATCH] Implement security group rule task --- upup/pkg/fi/cloudup/openstack/BUILD.bazel | 1 + upup/pkg/fi/cloudup/openstack/cloud.go | 52 ++++++ .../pkg/fi/cloudup/openstacktasks/BUILD.bazel | 2 + .../openstacktasks/securitygrouprule.go | 165 ++++++++++++++++++ 4 files changed, 220 insertions(+) create mode 100644 upup/pkg/fi/cloudup/openstacktasks/securitygrouprule.go diff --git a/upup/pkg/fi/cloudup/openstack/BUILD.bazel b/upup/pkg/fi/cloudup/openstack/BUILD.bazel index f5c509a3c1..389c9e6ba0 100644 --- a/upup/pkg/fi/cloudup/openstack/BUILD.bazel +++ b/upup/pkg/fi/cloudup/openstack/BUILD.bazel @@ -18,6 +18,7 @@ go_library( "//vendor/github.com/gophercloud/gophercloud/openstack:go_default_library", "//vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes:go_default_library", "//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups:go_default_library", + "//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//vendor/k8s.io/kubernetes/federation/pkg/dnsprovider:go_default_library", diff --git a/upup/pkg/fi/cloudup/openstack/cloud.go b/upup/pkg/fi/cloudup/openstack/cloud.go index d9f197090a..36873289c8 100644 --- a/upup/pkg/fi/cloudup/openstack/cloud.go +++ b/upup/pkg/fi/cloudup/openstack/cloud.go @@ -25,6 +25,7 @@ import ( os "github.com/gophercloud/gophercloud/openstack" cinder "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" sg "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups" + sgr "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules" "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/kops/pkg/apis/kops" @@ -74,6 +75,12 @@ type OpenstackCloud interface { //CreateSecurityGroup will create a new Neutron security group CreateSecurityGroup(opt sg.CreateOpts) (*sg.SecGroup, error) + + //ListSecurityGroupRules will return the Neutron security group rules which match the options + ListSecurityGroupRules(opt sgr.ListOpts) ([]sgr.SecGroupRule, error) + + //CreateSecurityGroupRule will create a new Neutron security group rule + CreateSecurityGroupRule(opt sgr.CreateOpts) (*sgr.SecGroupRule, error) } type openstackCloud struct { @@ -265,3 +272,48 @@ func (c *openstackCloud) CreateSecurityGroup(opt sg.CreateOpts) (*sg.SecGroup, e return group, wait.ErrWaitTimeout } } + +func (c *openstackCloud) ListSecurityGroupRules(opt sgr.ListOpts) ([]sgr.SecGroupRule, error) { + var rules []sgr.SecGroupRule + + done, err := vfs.RetryWithBackoff(readBackoff, func() (bool, error) { + allPages, err := sgr.List(c.neutronClient, opt).AllPages() + if err != nil { + return false, fmt.Errorf("error listing security group rules %v: %v", opt, err) + } + + rs, err := sgr.ExtractRules(allPages) + if err != nil { + return false, fmt.Errorf("error extracting security group rules from pages: %v", err) + } + rules = rs + return true, nil + }) + if err != nil { + return rules, err + } else if done { + return rules, nil + } else { + return rules, wait.ErrWaitTimeout + } +} + +func (c *openstackCloud) CreateSecurityGroupRule(opt sgr.CreateOpts) (*sgr.SecGroupRule, error) { + var rule *sgr.SecGroupRule + + done, err := vfs.RetryWithBackoff(writeBackoff, func() (bool, error) { + r, err := sgr.Create(c.neutronClient, opt).Extract() + if err != nil { + return false, fmt.Errorf("error creating security group rule %v: %v", opt, err) + } + rule = r + return true, nil + }) + if err != nil { + return rule, err + } else if done { + return rule, nil + } else { + return rule, wait.ErrWaitTimeout + } +} diff --git a/upup/pkg/fi/cloudup/openstacktasks/BUILD.bazel b/upup/pkg/fi/cloudup/openstacktasks/BUILD.bazel index 2d8e2242e0..a8c3266ecd 100644 --- a/upup/pkg/fi/cloudup/openstacktasks/BUILD.bazel +++ b/upup/pkg/fi/cloudup/openstacktasks/BUILD.bazel @@ -4,6 +4,7 @@ go_library( name = "go_default_library", srcs = [ "securitygroup.go", + "securitygrouprule.go", "volume.go", ], importpath = "k8s.io/kops/upup/pkg/fi/cloudup/openstacktasks", @@ -14,5 +15,6 @@ go_library( "//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes:go_default_library", "//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups:go_default_library", + "//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules:go_default_library", ], ) diff --git a/upup/pkg/fi/cloudup/openstacktasks/securitygrouprule.go b/upup/pkg/fi/cloudup/openstacktasks/securitygrouprule.go new file mode 100644 index 0000000000..7a5c2a034c --- /dev/null +++ b/upup/pkg/fi/cloudup/openstacktasks/securitygrouprule.go @@ -0,0 +1,165 @@ +/* +Copyright 2017 The Kubernetes 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 openstacktasks + +import ( + "fmt" + + "github.com/golang/glog" + sgr "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules" + "k8s.io/kops/upup/pkg/fi" + "k8s.io/kops/upup/pkg/fi/cloudup/openstack" +) + +func Int(v int) *int { + return &v +} + +func IntValue(v *int) int { + if v == nil { + return 0 + } + return *v +} + +type SecurityGroupRule struct { + ID *string + Direction *string + EtherType *string + SecGroup *SecurityGroup + PortRangeMin *int + PortRangeMax *int + Protocol *string + RemoteIPPrefix *string + Lifecycle *fi.Lifecycle +} + +var _ fi.CompareWithID = &SecurityGroupRule{} + +func (r *SecurityGroupRule) CompareWithID() *string { + return r.ID +} + +func (r *SecurityGroupRule) Find(context *fi.Context) (*SecurityGroupRule, error) { + if r.SecGroup == nil || r.SecGroup.ID == nil { + return nil, nil + } + + cloud := context.Cloud.(openstack.OpenstackCloud) + + opt := sgr.ListOpts{ + Direction: fi.StringValue(r.Direction), + EtherType: fi.StringValue(r.EtherType), + PortRangeMax: IntValue(r.PortRangeMax), + PortRangeMin: IntValue(r.PortRangeMin), + Protocol: fi.StringValue(r.Protocol), + RemoteIPPrefix: fi.StringValue(r.RemoteIPPrefix), + SecGroupID: fi.StringValue(r.SecGroup.ID), + } + rs, err := cloud.ListSecurityGroupRules(opt) + if err != nil { + return nil, err + } + n := len(rs) + if n == 0 { + return nil, nil + } else if n != 1 { + return nil, fmt.Errorf("found multiple SecurityGroupRules") + } + rule := rs[0] + actual := &SecurityGroupRule{ + ID: fi.String(rule.ID), + Direction: fi.String(rule.Direction), + EtherType: fi.String(rule.EtherType), + PortRangeMax: Int(rule.PortRangeMax), + PortRangeMin: Int(rule.PortRangeMin), + Protocol: fi.String(rule.Protocol), + RemoteIPPrefix: fi.String(rule.RemoteIPPrefix), + SecGroup: &SecurityGroup{ID: fi.String(rule.SecGroupID)}, + Lifecycle: r.Lifecycle, + } + return actual, nil +} + +func (r *SecurityGroupRule) Run(context *fi.Context) error { + return fi.DefaultDeltaRunMethod(r, context) +} + +func (_ *SecurityGroupRule) CheckChanges(a, e, changes *SecurityGroupRule) error { + if a == nil { + if e.Direction == nil { + return fi.RequiredField("Direction") + } + if e.EtherType == nil { + return fi.RequiredField("EtherType") + } + if e.SecGroup == nil { + return fi.RequiredField("SecGroup") + } + } else { + if changes.ID != nil { + return fi.CannotChangeField("ID") + } + if changes.Direction != nil { + return fi.CannotChangeField("Direction") + } + if changes.EtherType != nil { + return fi.CannotChangeField("EtherType") + } + if changes.SecGroup != nil { + return fi.CannotChangeField("SecGroup") + } + } + return nil +} + +func (_ *SecurityGroupRule) RenderOpenstack(t *openstack.OpenstackAPITarget, a, e, changes *SecurityGroupRule) error { + if a == nil { + glog.V(2).Infof("Creating SecurityGroupRule") + + opt := sgr.CreateOpts{ + Direction: sgr.RuleDirection(fi.StringValue(e.Direction)), + EtherType: sgr.RuleEtherType(fi.StringValue(e.EtherType)), + SecGroupID: fi.StringValue(e.SecGroup.ID), + PortRangeMax: IntValue(e.PortRangeMax), + PortRangeMin: IntValue(e.PortRangeMin), + Protocol: sgr.RuleProtocol(fi.StringValue(e.Protocol)), + RemoteIPPrefix: fi.StringValue(e.RemoteIPPrefix), + } + + r, err := t.Cloud.CreateSecurityGroupRule(opt) + if err != nil { + return fmt.Errorf("error creating SecurityGroupRule: %v", err) + } + + e.ID = fi.String(r.ID) + return nil + } + + glog.V(2).Infof("Openstack task SecurityGroupRule::RenderOpenstack did nothing") + return nil +} + +var _ fi.HasLifecycle = &SecurityGroupRule{} + +func (r *SecurityGroupRule) GetLifecycle() *fi.Lifecycle { + return r.Lifecycle +} + +func (r *SecurityGroupRule) String() string { + return fi.TaskAsString(r) +}