"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "istioctl/cmd/add-to-mesh.go" between
istio-1.5.4.tar.gz and istio-1.6.0.tar.gz

About: Istio is a platform-independent service mesh that provides a uniform way to connect, secure, control, and observe microservices.

add-to-mesh.go  (istio-1.5.4):add-to-mesh.go  (istio-1.6.0)
skipping to change at line 18 skipping to change at line 18
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package cmd package cmd
import ( import (
"context"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"strconv" "strconv"
"strings" "strings"
"github.com/ghodss/yaml" "github.com/ghodss/yaml"
"github.com/hashicorp/go-multierror" "github.com/hashicorp/go-multierror"
"github.com/spf13/cobra" "github.com/spf13/cobra"
skipping to change at line 100 skipping to change at line 101
"injection values configuration filename.") "injection values configuration filename.")
addToMeshCmd.PersistentFlags().StringVar(&meshConfigMapName, "meshConfigM apName", defaultMeshConfigMapName, addToMeshCmd.PersistentFlags().StringVar(&meshConfigMapName, "meshConfigM apName", defaultMeshConfigMapName,
fmt.Sprintf("ConfigMap name for Istio mesh configuration, key sho uld be %q", configMapKey)) fmt.Sprintf("ConfigMap name for Istio mesh configuration, key sho uld be %q", configMapKey))
addToMeshCmd.PersistentFlags().StringVar(&injectConfigMapName, "injectCon figMapName", defaultInjectConfigMapName, addToMeshCmd.PersistentFlags().StringVar(&injectConfigMapName, "injectCon figMapName", defaultInjectConfigMapName,
fmt.Sprintf("ConfigMap name for Istio sidecar injection, key shou ld be %q.", injectConfigMapKey)) fmt.Sprintf("ConfigMap name for Istio sidecar injection, key shou ld be %q.", injectConfigMapKey))
return addToMeshCmd return addToMeshCmd
} }
func deploymentMeshifyCmd() *cobra.Command { func deploymentMeshifyCmd() *cobra.Command {
var revision string
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "deployment", Use: "deployment",
Short: "Add deployment to Istio service mesh", Short: "Add deployment to Istio service mesh",
Long: `istioctl experimental add-to-mesh deployment restarts pods with the Istio sidecar. Use 'add-to-mesh' Long: `istioctl experimental add-to-mesh deployment restarts pods with the Istio sidecar. Use 'add-to-mesh'
to test deployments for compatibility with Istio. If your deployment does not f unction after to test deployments for compatibility with Istio. If your deployment does not f unction after
using 'add-to-mesh' you must re-deploy it and troubleshoot it for Istio compatib ility. using 'add-to-mesh' you must re-deploy it and troubleshoot it for Istio compatib ility.
See https://istio.io/docs/setup/kubernetes/additional-setup/requirements/ See https://istio.io/docs/setup/kubernetes/additional-setup/requirements/
THIS COMMAND IS STILL UNDER ACTIVE DEVELOPMENT AND NOT READY FOR PRODUCTION USE. THIS COMMAND IS STILL UNDER ACTIVE DEVELOPMENT AND NOT READY FOR PRODUCTION USE.
`, `,
Example: `istioctl experimental add-to-mesh deployment productpag e-v1`, Example: `istioctl experimental add-to-mesh deployment productpag e-v1`,
skipping to change at line 126 skipping to change at line 129
return err return err
} }
var sidecarTemplate, valuesConfig string var sidecarTemplate, valuesConfig string
ns := handlers.HandleNamespace(namespace, defaultNamespac e) ns := handlers.HandleNamespace(namespace, defaultNamespac e)
writer := cmd.OutOrStdout() writer := cmd.OutOrStdout()
meshConfig, err := setupParameters(&sidecarTemplate, &val uesConfig) meshConfig, err := setupParameters(&sidecarTemplate, &val uesConfig)
if err != nil { if err != nil {
return err return err
} }
dep, err := client.AppsV1().Deployments(ns).Get(args[0], metav1.GetOptions{}) dep, err := client.AppsV1().Deployments(ns).Get(context.T ODO(), args[0], metav1.GetOptions{})
if err != nil { if err != nil {
return fmt.Errorf("deployment %q does not exist", args[0]) return fmt.Errorf("deployment %q does not exist", args[0])
} }
deps := make([]appsv1.Deployment, 0) deps := make([]appsv1.Deployment, 0)
deps = append(deps, *dep) deps = append(deps, *dep)
return injectSideCarIntoDeployment(client, deps, sidecarT emplate, valuesConfig, return injectSideCarIntoDeployment(client, deps, sidecarT emplate, valuesConfig,
args[0], ns, meshConfig, writer) args[0], ns, revision, meshConfig, writer)
}, },
} }
cmd.PersistentFlags().StringVar(&revision, "revision", "",
"control plane revision")
return cmd return cmd
} }
func svcMeshifyCmd() *cobra.Command { func svcMeshifyCmd() *cobra.Command {
var revision string
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "service", Use: "service",
Short: "Add Service to Istio service mesh", Short: "Add Service to Istio service mesh",
Long: `istioctl experimental add-to-mesh service restarts pods wi th the Istio sidecar. Use 'add-to-mesh' Long: `istioctl experimental add-to-mesh service restarts pods wi th the Istio sidecar. Use 'add-to-mesh'
to test deployments for compatibility with Istio. If your service does not func tion after to test deployments for compatibility with Istio. If your service does not func tion after
using 'add-to-mesh' you must re-deploy it and troubleshoot it for Istio compatib ility. using 'add-to-mesh' you must re-deploy it and troubleshoot it for Istio compatib ility.
See https://istio.io/docs/setup/kubernetes/additional-setup/requirements/ See https://istio.io/docs/setup/kubernetes/additional-setup/requirements/
THIS COMMAND IS STILL UNDER ACTIVE DEVELOPMENT AND NOT READY FOR PRODUCTION USE. THIS COMMAND IS STILL UNDER ACTIVE DEVELOPMENT AND NOT READY FOR PRODUCTION USE.
`, `,
Example: `istioctl experimental add-to-mesh service productpage`, Example: `istioctl experimental add-to-mesh service productpage`,
skipping to change at line 175 skipping to change at line 184
} }
matchingDeployments, err := findDeploymentsForSvc(client, ns, args[0]) matchingDeployments, err := findDeploymentsForSvc(client, ns, args[0])
if err != nil { if err != nil {
return err return err
} }
if len(matchingDeployments) == 0 { if len(matchingDeployments) == 0 {
_, _ = fmt.Fprintf(writer, "No deployments found for service %s.%s\n", args[0], ns) _, _ = fmt.Fprintf(writer, "No deployments found for service %s.%s\n", args[0], ns)
return nil return nil
} }
return injectSideCarIntoDeployment(client, matchingDeploy ments, sidecarTemplate, valuesConfig, return injectSideCarIntoDeployment(client, matchingDeploy ments, sidecarTemplate, valuesConfig,
args[0], ns, meshConfig, writer) args[0], ns, revision, meshConfig, writer)
}, },
} }
cmd.PersistentFlags().StringVar(&revision, "revision", "",
"control plane revision")
return cmd return cmd
} }
func externalSvcMeshifyCmd() *cobra.Command { func externalSvcMeshifyCmd() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "external-service <svcname> <ip>... [name1:]port1 [name2:] port2 ...", Use: "external-service <svcname> <ip>... [name1:]port1 [name2:] port2 ...",
Short: "Add external service(eg:services running on VM) to Istio service mesh", Short: "Add external service(eg:services running on VM) to Istio service mesh",
Long: `istioctl experimental add-to-mesh external-service create a ServiceEntry and\ Long: `istioctl experimental add-to-mesh external-service create a ServiceEntry and\
a Service without selector for the specified external service in Istio service m esh. a Service without selector for the specified external service in Istio service m esh.
The typical usage scenario is Mesh Expansion on VMs. The typical usage scenario is Mesh Expansion on VMs.
skipping to change at line 206 skipping to change at line 219
client, err := interfaceFactory(kubeconfig) client, err := interfaceFactory(kubeconfig)
if err != nil { if err != nil {
return err return err
} }
seClient, err := crdFactory(kubeconfig) seClient, err := crdFactory(kubeconfig)
if err != nil { if err != nil {
return err return err
} }
writer := cmd.OutOrStdout() writer := cmd.OutOrStdout()
ns := handlers.HandleNamespace(namespace, defaultNamespac e) ns := handlers.HandleNamespace(namespace, defaultNamespac e)
_, err = client.CoreV1().Services(ns).Get(args[0], metav1 .GetOptions{}) _, err = client.CoreV1().Services(ns).Get(context.TODO(), args[0], metav1.GetOptions{})
if err != nil { if err != nil {
return addServiceOnVMToMesh(seClient, client, ns, args, labels, annotations, svcAcctAnn, writer) return addServiceOnVMToMesh(seClient, client, ns, args, labels, annotations, svcAcctAnn, writer)
} }
return fmt.Errorf("service %q already exists, skip", args [0]) return fmt.Errorf("service %q already exists, skip", args [0])
}, },
} }
cmd.PersistentFlags().StringSliceVarP(&labels, "labels", "l", cmd.PersistentFlags().StringSliceVarP(&labels, "labels", "l",
nil, "List of labels to apply if creating a service/endpoint; e.g . -l env=prod,vers=2") nil, "List of labels to apply if creating a service/endpoint; e.g . -l env=prod,vers=2")
cmd.PersistentFlags().StringSliceVarP(&annotations, "annotations", "a", cmd.PersistentFlags().StringSliceVarP(&annotations, "annotations", "a",
nil, "List of string annotations to apply if creating a service/e ndpoint; e.g. -a foo=bar,x=y") nil, "List of string annotations to apply if creating a service/e ndpoint; e.g. -a foo=bar,x=y")
skipping to change at line 260 skipping to change at line 273
return nil, err return nil, err
} }
*valuesConfig = string(valuesConfigBytes) *valuesConfig = string(valuesConfigBytes)
} else if *valuesConfig, err = getValuesFromConfigMap(kubeconfig); err != nil { } else if *valuesConfig, err = getValuesFromConfigMap(kubeconfig); err != nil {
return nil, err return nil, err
} }
return meshConfig, err return meshConfig, err
} }
func injectSideCarIntoDeployment(client kubernetes.Interface, deps []appsv1.Depl oyment, sidecarTemplate, valuesConfig, func injectSideCarIntoDeployment(client kubernetes.Interface, deps []appsv1.Depl oyment, sidecarTemplate, valuesConfig,
svcName, svcNamespace string, meshConfig *meshconfig.MeshConfig, writer i o.Writer) error { svcName, svcNamespace string, revision string, meshConfig *meshconfig.Mes hConfig, writer io.Writer) error {
var errs error var errs error
for _, dep := range deps { for _, dep := range deps {
log.Debugf("updating deployment %s.%s with Istio sidecar injected ", log.Debugf("updating deployment %s.%s with Istio sidecar injected ",
dep.Name, dep.Namespace) dep.Name, dep.Namespace)
newDep, err := inject.IntoObject(sidecarTemplate, valuesConfig, m eshConfig, &dep) newDep, err := inject.IntoObject(sidecarTemplate, valuesConfig, r evision, meshConfig, &dep)
if err != nil { if err != nil {
errs = multierror.Append(errs, fmt.Errorf("failed to upda te deployment %s.%s for service %s.%s due to %v", errs = multierror.Append(errs, fmt.Errorf("failed to inje ct sidecar to deployment resource %s.%s for service %s.%s due to %v",
dep.Name, dep.Namespace, svcName, svcNamespace, e rr)) dep.Name, dep.Namespace, svcName, svcNamespace, e rr))
continue continue
} }
res, b := newDep.(*appsv1.Deployment) res, b := newDep.(*appsv1.Deployment)
if !b { if !b {
errs = multierror.Append(errs, fmt.Errorf("failed to upda errs = multierror.Append(errs, fmt.Errorf("failed to crea
te deployment %s.%s for service %s.%s", te new deployment resource %s.%s for service %s.%s due to %v",
dep.Name, dep.Namespace, svcName, svcNamespace)) dep.Name, dep.Namespace, svcName, svcNamespace, e
rr))
continue continue
} }
if _, err = if _, err =
client.AppsV1().Deployments(svcNamespace).Update(res); er r != nil { client.AppsV1().Deployments(svcNamespace).Update(context. TODO(), res, metav1.UpdateOptions{}); err != nil {
errs = multierror.Append(errs, fmt.Errorf("failed to upda te deployment %s.%s for service %s.%s due to %v", errs = multierror.Append(errs, fmt.Errorf("failed to upda te deployment %s.%s for service %s.%s due to %v",
dep.Name, dep.Namespace, svcName, svcNamespace, e rr)) dep.Name, dep.Namespace, svcName, svcNamespace, e rr))
continue continue
} }
d := &appsv1.Deployment{ d := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: dep.Name, Name: dep.Name,
Namespace: dep.Namespace, Namespace: dep.Namespace,
UID: dep.UID, UID: dep.UID,
}, },
} }
if _, err = client.AppsV1().Deployments(svcNamespace).UpdateStatu if _, err = client.AppsV1().Deployments(svcNamespace).UpdateStatu
s(d); err != nil { s(context.TODO(), d, metav1.UpdateOptions{}); err != nil {
errs = multierror.Append(errs, fmt.Errorf("failed to upda errs = multierror.Append(errs, fmt.Errorf("failed to upda
te deployment %s.%s for service %s.%s due to %v", te deployment status %s.%s for service %s.%s due to %v",
dep.Name, dep.Namespace, svcName, svcNamespace, e rr)) dep.Name, dep.Namespace, svcName, svcNamespace, e rr))
continue continue
} }
_, _ = fmt.Fprintf(writer, "deployment %s.%s updated successfully with Istio sidecar injected.\n"+ _, _ = fmt.Fprintf(writer, "deployment %s.%s updated successfully with Istio sidecar injected.\n"+
"Next Step: Add related labels to the deployment to align with Istio's requirement: "+ "Next Step: Add related labels to the deployment to align with Istio's requirement: "+
"https://istio.io/docs/setup/kubernetes/additional-setup/ requirements/\n", "https://istio.io/docs/setup/kubernetes/additional-setup/ requirements/\n",
dep.Name, dep.Namespace) dep.Name, dep.Namespace)
} }
return errs return errs
} }
func findDeploymentsForSvc(client kubernetes.Interface, ns, name string) ([]apps v1.Deployment, error) { func findDeploymentsForSvc(client kubernetes.Interface, ns, name string) ([]apps v1.Deployment, error) {
deps := make([]appsv1.Deployment, 0) deps := make([]appsv1.Deployment, 0)
svc, err := client.CoreV1().Services(ns).Get(name, metav1.GetOptions{}) svc, err := client.CoreV1().Services(ns).Get(context.TODO(), name, metav1 .GetOptions{})
if err != nil { if err != nil {
return nil, err return nil, err
} }
svcSelector := k8s_labels.SelectorFromSet(svc.Spec.Selector) svcSelector := k8s_labels.SelectorFromSet(svc.Spec.Selector)
if svcSelector.Empty() { if svcSelector.Empty() {
return nil, nil return nil, nil
} }
deployments, err := client.AppsV1().Deployments(ns).List(metav1.ListOptio ns{}) deployments, err := client.AppsV1().Deployments(ns).List(context.TODO(), metav1.ListOptions{})
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, dep := range deployments.Items { for _, dep := range deployments.Items {
depLabels := k8s_labels.Set(dep.Spec.Selector.MatchLabels) depLabels := k8s_labels.Set(dep.Spec.Selector.MatchLabels)
if svcSelector.Matches(depLabels) { if svcSelector.Matches(depLabels) {
deps = append(deps, dep) deps = append(deps, dep)
} }
} }
return deps, nil return deps, nil
skipping to change at line 403 skipping to change at line 416
s := &corev1.Service{ s := &corev1.Service{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: opts.Name, Name: opts.Name,
Namespace: opts.Namespace, Namespace: opts.Namespace,
Annotations: annotations, Annotations: annotations,
Labels: labels, Labels: labels,
}, },
} }
// Pre-check Kubernetes service and service entry does not exist. // Pre-check Kubernetes service and service entry does not exist.
_, err = client.CoreV1().Services(ns).Get(opts.Name, metav1.GetOptions{}) _, err = client.CoreV1().Services(ns).Get(context.TODO(), opts.Name, meta v1.GetOptions{})
if err == nil { if err == nil {
return fmt.Errorf("service %q already exists, skip", opts.Name) return fmt.Errorf("service %q already exists, skip", opts.Name)
} }
serviceEntryGVR := schema.GroupVersionResource{ serviceEntryGVR := schema.GroupVersionResource{
Group: collections.IstioNetworkingV1Alpha3Serviceentries.Resou rce().Group(), Group: collections.IstioNetworkingV1Alpha3Serviceentries.Resou rce().Group(),
Version: collections.IstioNetworkingV1Alpha3Serviceentries.Resou rce().Version(), Version: collections.IstioNetworkingV1Alpha3Serviceentries.Resou rce().Version(),
Resource: collections.IstioNetworkingV1Alpha3Serviceentries.Resou rce().Plural(), Resource: collections.IstioNetworkingV1Alpha3Serviceentries.Resou rce().Plural(),
} }
_, err = dynamicClient.Resource(serviceEntryGVR).Namespace(ns).Get(resour ceName(opts.Name), metav1.GetOptions{}) _, err = dynamicClient.Resource(serviceEntryGVR).Namespace(ns).Get(contex t.TODO(), resourceName(opts.Name), metav1.GetOptions{})
if err == nil { if err == nil {
return fmt.Errorf("service entry %q already exists, skip", resour ceName(opts.Name)) return fmt.Errorf("service entry %q already exists, skip", resour ceName(opts.Name))
} }
if err = generateServiceEntry(u, opts); err != nil { if err = generateServiceEntry(u, opts); err != nil {
return err return err
} }
generateK8sService(s, opts) generateK8sService(s, opts)
if err = createServiceEntry(dynamicClient, ns, u, opts.Name, writer); err != nil { if err = createServiceEntry(dynamicClient, ns, u, opts.Name, writer); err != nil {
return err return err
skipping to change at line 439 skipping to change at line 452
return fmt.Errorf("empty vm service options") return fmt.Errorf("empty vm service options")
} }
ports := make([]*v1alpha3.Port, 0) ports := make([]*v1alpha3.Port, 0)
for _, p := range o.PortList { for _, p := range o.PortList {
ports = append(ports, &v1alpha3.Port{ ports = append(ports, &v1alpha3.Port{
Number: uint32(p.Port), Number: uint32(p.Port),
Protocol: string(p.Protocol), Protocol: string(p.Protocol),
Name: p.Name, Name: p.Name,
}) })
} }
eps := make([]*v1alpha3.ServiceEntry_Endpoint, 0) eps := make([]*v1alpha3.WorkloadEntry, 0)
for _, ip := range o.IP { for _, ip := range o.IP {
eps = append(eps, &v1alpha3.ServiceEntry_Endpoint{ eps = append(eps, &v1alpha3.WorkloadEntry{
Address: ip, Address: ip,
Labels: o.Labels, Labels: o.Labels,
}) })
} }
host := fmt.Sprintf("%v.%v.svc.cluster.local", o.Name, o.Namespace) host := fmt.Sprintf("%v.%v.svc.cluster.local", o.Name, o.Namespace)
spec := &v1alpha3.ServiceEntry{ spec := &v1alpha3.ServiceEntry{
Hosts: []string{host}, Hosts: []string{host},
Ports: ports, Ports: ports,
Endpoints: eps, Endpoints: eps,
Resolution: v1alpha3.ServiceEntry_STATIC, Resolution: v1alpha3.ServiceEntry_STATIC,
skipping to change at line 521 skipping to change at line 534
k = str k = str
} }
return k, v return k, v
} }
// createK8sService creates k8s service object for external services in order fo r DNS query and cluster VIP. // createK8sService creates k8s service object for external services in order fo r DNS query and cluster VIP.
func createK8sService(client kubernetes.Interface, ns string, svc *corev1.Servic e, writer io.Writer) error { func createK8sService(client kubernetes.Interface, ns string, svc *corev1.Servic e, writer io.Writer) error {
if svc == nil { if svc == nil {
return fmt.Errorf("failed to create vm service") return fmt.Errorf("failed to create vm service")
} }
if _, err := client.CoreV1().Services(ns).Create(svc); err != nil { if _, err := client.CoreV1().Services(ns).Create(context.TODO(), svc, met av1.CreateOptions{}); err != nil {
return fmt.Errorf("failed to create kuberenetes service %v", err) return fmt.Errorf("failed to create kuberenetes service %v", err)
} }
if _, err := client.CoreV1().Services(ns).UpdateStatus(svc); err != nil { if _, err := client.CoreV1().Services(ns).UpdateStatus(context.TODO(), sv c, metav1.UpdateOptions{}); err != nil {
return fmt.Errorf("failed to create kuberenetes service %v", err) return fmt.Errorf("failed to create kuberenetes service %v", err)
} }
sName := strings.Join([]string{svc.Name, svc.Namespace}, ".") sName := strings.Join([]string{svc.Name, svc.Namespace}, ".")
_, _ = fmt.Fprintf(writer, "Kubernetes Service %q has been created in the Istio service mesh"+ _, _ = fmt.Fprintf(writer, "Kubernetes Service %q has been created in the Istio service mesh"+
" for the external service %q\n", sName, svc.Name) " for the external service %q\n", sName, svc.Name)
return nil return nil
} }
// createServiceEntry creates an Istio ServiceEntry object in order to register vm service. // createServiceEntry creates an Istio ServiceEntry object in order to register vm service.
func createServiceEntry(dynamicClient dynamic.Interface, ns string, func createServiceEntry(dynamicClient dynamic.Interface, ns string,
u *unstructured.Unstructured, name string, writer io.Writer) error { u *unstructured.Unstructured, name string, writer io.Writer) error {
if u == nil { if u == nil {
return fmt.Errorf("failed to create vm service") return fmt.Errorf("failed to create vm service")
} }
serviceEntryGVR := schema.GroupVersionResource{ serviceEntryGVR := schema.GroupVersionResource{
Group: collections.IstioNetworkingV1Alpha3Serviceentries.Resou rce().Group(), Group: collections.IstioNetworkingV1Alpha3Serviceentries.Resou rce().Group(),
Version: collections.IstioNetworkingV1Alpha3Serviceentries.Resou rce().Version(), Version: collections.IstioNetworkingV1Alpha3Serviceentries.Resou rce().Version(),
Resource: collections.IstioNetworkingV1Alpha3Serviceentries.Resou rce().Plural(), Resource: collections.IstioNetworkingV1Alpha3Serviceentries.Resou rce().Plural(),
} }
_, err := dynamicClient.Resource(serviceEntryGVR).Namespace(ns).Create(u, metav1.CreateOptions{}) _, err := dynamicClient.Resource(serviceEntryGVR).Namespace(ns).Create(co ntext.TODO(), u, metav1.CreateOptions{})
if err != nil { if err != nil {
return fmt.Errorf("failed to create service entry %v", err) return fmt.Errorf("failed to create service entry %v", err)
} }
seName := strings.Join([]string{u.GetName(), u.GetNamespace()}, ".") seName := strings.Join([]string{u.GetName(), u.GetNamespace()}, ".")
_, _ = fmt.Fprintf(writer, "ServiceEntry %q has been created in the Istio service mesh"+ _, _ = fmt.Fprintf(writer, "ServiceEntry %q has been created in the Istio service mesh"+
" for the external service %q\n", seName, name) " for the external service %q\n", seName, name)
return nil return nil
} }
 End of changes. 24 change blocks. 
24 lines changed or deleted 38 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)